From b97ea5e55b5094f803100cdeead1779f617a2a64 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Fri, 27 Dec 2019 14:33:50 -0800 Subject: [PATCH 01/10] wip --- .../report/html/renderer/category-renderer.js | 20 ++++++++++--------- .../html/renderer/crc-details-renderer.js | 4 ++-- .../report/html/renderer/details-renderer.js | 9 +++++++-- .../renderer/performance-category-renderer.js | 7 ++++--- .../report/html/renderer/report-renderer.js | 13 ++---------- .../html/renderer/report-ui-features.js | 2 +- .../report/html/renderer/snippet-renderer.js | 2 +- lighthouse-core/report/html/renderer/util.js | 13 ++++++------ types/lhr.d.ts | 7 ++++--- 9 files changed, 38 insertions(+), 39 deletions(-) diff --git a/lighthouse-core/report/html/renderer/category-renderer.js b/lighthouse-core/report/html/renderer/category-renderer.js index 56f0196a5f1c..eb20b0511c9c 100644 --- a/lighthouse-core/report/html/renderer/category-renderer.js +++ b/lighthouse-core/report/html/renderer/category-renderer.js @@ -45,10 +45,10 @@ class CategoryRenderer { */ get _clumpTitles() { return { - warning: Util.UIStrings.warningAuditsGroupTitle, - manual: Util.UIStrings.manualAuditsGroupTitle, - passed: Util.UIStrings.passedAuditsGroupTitle, - notApplicable: Util.UIStrings.notApplicableAuditsGroupTitle, + warning: this.detailsRenderer.strings.warningAuditsGroupTitle, + manual: this.detailsRenderer.strings.manualAuditsGroupTitle, + passed: this.detailsRenderer.strings.passedAuditsGroupTitle, + notApplicable: this.detailsRenderer.strings.notApplicableAuditsGroupTitle, }; } @@ -68,6 +68,7 @@ class CategoryRenderer { * @return {Element} */ populateAuditValues(audit, tmpl) { + const strings = this.detailsRenderer.strings; const auditEl = this.dom.find('.lh-audit', tmpl); auditEl.id = audit.result.id; const scoreDisplayMode = audit.result.scoreDisplayMode; @@ -115,10 +116,11 @@ class CategoryRenderer { if (audit.result.scoreDisplayMode === 'error') { auditEl.classList.add(`lh-audit--error`); const textEl = this.dom.find('.lh-audit__display-text', auditEl); - textEl.textContent = Util.UIStrings.errorLabel; + textEl.textContent = strings.errorLabel; textEl.classList.add('tooltip-boundary'); const tooltip = this.dom.createChildOf(textEl, 'div', 'tooltip tooltip--error'); - tooltip.textContent = audit.result.errorMessage || Util.UIStrings.errorMissingAuditInfo; + tooltip.textContent = + audit.result.errorMessage || strings.errorMissingAuditInfo; } else if (audit.result.explanation) { const explEl = this.dom.createChildOf(titleEl, 'div', 'lh-audit-explanation'); explEl.textContent = audit.result.explanation; @@ -128,7 +130,7 @@ class CategoryRenderer { // Add list of warnings or singular warning const warningsEl = this.dom.createChildOf(titleEl, 'div', 'lh-warnings'); - this.dom.createChildOf(warningsEl, 'span').textContent = Util.UIStrings.warningHeader; + this.dom.createChildOf(warningsEl, 'span').textContent = strings.warningHeader; if (warnings.length === 1) { warningsEl.appendChild(this.dom.document().createTextNode(warnings.join(''))); } else { @@ -287,7 +289,7 @@ class CategoryRenderer { const summaryInnerEl = this.dom.find('.lh-audit-group__summary', clumpElement); const chevronEl = summaryInnerEl.appendChild(this._createChevron()); - chevronEl.title = Util.UIStrings.auditGroupExpandTooltip; + chevronEl.title = this.detailsRenderer.strings.auditGroupExpandTooltip; const headerEl = this.dom.find('.lh-audit-group__header', clumpElement); const title = this._clumpTitles[clumpId]; @@ -345,7 +347,7 @@ class CategoryRenderer { percentageEl.textContent = scoreOutOf100.toString(); if (category.score === null) { percentageEl.textContent = '?'; - percentageEl.title = Util.UIStrings.errorLabel; + percentageEl.title = this.detailsRenderer.strings.errorLabel; } this.dom.find('.lh-gauge__label', tmpl).textContent = category.title; diff --git a/lighthouse-core/report/html/renderer/crc-details-renderer.js b/lighthouse-core/report/html/renderer/crc-details-renderer.js index 0c53f0865510..031be3dfc04f 100644 --- a/lighthouse-core/report/html/renderer/crc-details-renderer.js +++ b/lighthouse-core/report/html/renderer/crc-details-renderer.js @@ -172,9 +172,9 @@ class CriticalRequestChainRenderer { const containerEl = dom.find('.lh-crc', tmpl); // Fill in top summary. - dom.find('.crc-initial-nav', tmpl).textContent = Util.UIStrings.crcInitialNavigation; + dom.find('.crc-initial-nav', tmpl).textContent = detailsRenderer.strings.crcInitialNavigation; dom.find('.lh-crc__longest_duration_label', tmpl).textContent = - Util.UIStrings.crcLongestDurationLabel; + detailsRenderer.strings.crcLongestDurationLabel; dom.find('.lh-crc__longest_duration', tmpl).textContent = Util.formatMilliseconds(details.longestChain.duration); diff --git a/lighthouse-core/report/html/renderer/details-renderer.js b/lighthouse-core/report/html/renderer/details-renderer.js index 179b7eedd9e8..14248c137186 100644 --- a/lighthouse-core/report/html/renderer/details-renderer.js +++ b/lighthouse-core/report/html/renderer/details-renderer.js @@ -25,14 +25,19 @@ const URL_PREFIXES = ['http://', 'https://', 'data:']; class DetailsRenderer { /** * @param {DOM} dom + * @param {LH.ReportResult} report */ - constructor(dom) { - /** @type {DOM} */ + constructor(dom, report) { this._dom = dom; + this._report = report; /** @type {ParentNode} */ this._templateContext; // eslint-disable-line no-unused-expressions } + get strings() { + return this._report.i18n.rendererFormattedStrings; + } + /** * @param {ParentNode} context */ diff --git a/lighthouse-core/report/html/renderer/performance-category-renderer.js b/lighthouse-core/report/html/renderer/performance-category-renderer.js index 51e2b630143e..99183c3bbe5b 100644 --- a/lighthouse-core/report/html/renderer/performance-category-renderer.js +++ b/lighthouse-core/report/html/renderer/performance-category-renderer.js @@ -112,6 +112,7 @@ class PerformanceCategoryRenderer extends CategoryRenderer { * @override */ render(category, groups, environment) { + const strings = this.detailsRenderer.strings; const element = this.dom.createElement('div', 'lh-category'); if (environment === 'PSI') { const gaugeEl = this.dom.createElement('div', 'lh-score__gauge'); @@ -148,7 +149,7 @@ class PerformanceCategoryRenderer extends CategoryRenderer { // 'Values are estimated and may vary' is used as the category description for PSI if (environment !== 'PSI') { const estValuesEl = this.dom.createChildOf(metricAuditsEl, 'div', 'lh-metrics__disclaimer'); - const disclaimerEl = this.dom.convertMarkdownLinkSnippets(Util.UIStrings.varianceDisclaimer); + const disclaimerEl = this.dom.convertMarkdownLinkSnippets(strings.varianceDisclaimer); estValuesEl.appendChild(disclaimerEl); } @@ -194,9 +195,9 @@ class PerformanceCategoryRenderer extends CategoryRenderer { const tmpl = this.dom.cloneTemplate('#tmpl-lh-opportunity-header', this.templateContext); this.dom.find('.lh-load-opportunity__col--one', tmpl).textContent = - Util.UIStrings.opportunityResourceColumnLabel; + strings.opportunityResourceColumnLabel; this.dom.find('.lh-load-opportunity__col--two', tmpl).textContent = - Util.UIStrings.opportunitySavingsColumnLabel; + strings.opportunitySavingsColumnLabel; const headerEl = this.dom.find('.lh-load-opportunity__header', tmpl); groupEl.appendChild(headerEl); diff --git a/lighthouse-core/report/html/renderer/report-renderer.js b/lighthouse-core/report/html/renderer/report-renderer.js index fe4779f265e8..a502bf232066 100644 --- a/lighthouse-core/report/html/renderer/report-renderer.js +++ b/lighthouse-core/report/html/renderer/report-renderer.js @@ -44,9 +44,6 @@ class ReportRenderer { * @return {Element} */ renderReport(result, container) { - // Mutate the UIStrings if necessary (while saving originals) - const originalUIStrings = JSON.parse(JSON.stringify(Util.UIStrings)); - this._dom.setLighthouseChannel(result.configSettings.channel || 'unknown'); const report = Util.prepareReportResult(result); @@ -54,9 +51,6 @@ class ReportRenderer { container.textContent = ''; // Remove previous report. container.appendChild(this._renderReport(report)); - // put the UIStrings back into original state - Util.updateAllUIStrings(originalUIStrings); - return container; } @@ -136,7 +130,7 @@ class ReportRenderer { const container = this._dom.cloneTemplate('#tmpl-lh-warnings--toplevel', this._templateContext); const message = this._dom.find('.lh-warnings__msg', container); - message.textContent = Util.UIStrings.toplevelWarningsMessage; + message.textContent = report.i18n.rendererFormattedStrings.toplevelWarningsMessage; const warnings = this._dom.find('ul', container); for (const warningString of report.runWarnings) { @@ -185,7 +179,7 @@ class ReportRenderer { * @return {DocumentFragment} */ _renderReport(report) { - const detailsRenderer = new DetailsRenderer(this._dom); + const detailsRenderer = new DetailsRenderer(this._dom, report); const categoryRenderer = new CategoryRenderer(this._dom, detailsRenderer); categoryRenderer.setTemplateContext(this._templateContext); @@ -249,9 +243,6 @@ class ReportRenderer { } } -/** @type {LH.I18NRendererStrings} */ -ReportRenderer._UIStringsStash = {}; - if (typeof module !== 'undefined' && module.exports) { module.exports = ReportRenderer; } else { diff --git a/lighthouse-core/report/html/renderer/report-ui-features.js b/lighthouse-core/report/html/renderer/report-ui-features.js index 1ce201196776..5658602b2e64 100644 --- a/lighthouse-core/report/html/renderer/report-ui-features.js +++ b/lighthouse-core/report/html/renderer/report-ui-features.js @@ -252,7 +252,7 @@ class ReportUIFeatures { this._dom.find('.lh-3p-filter-count', filterTemplate).textContent = `${thirdPartyRows.size}`; this._dom.find('.lh-3p-ui-string', filterTemplate).textContent = - Util.UIStrings.thirdPartyResourcesLabel; + this.json.i18n.rendererFormattedStrings.thirdPartyResourcesLabel; // If all or none of the rows are 3rd party, disable the checkbox. if (thirdPartyRows.size === urlItems.length || !thirdPartyRows.size) { diff --git a/lighthouse-core/report/html/renderer/snippet-renderer.js b/lighthouse-core/report/html/renderer/snippet-renderer.js index 66fd015f4dc7..b618cb61656c 100644 --- a/lighthouse-core/report/html/renderer/snippet-renderer.js +++ b/lighthouse-core/report/html/renderer/snippet-renderer.js @@ -105,7 +105,7 @@ class SnippetRenderer { const { snippetCollapseButtonLabel, snippetExpandButtonLabel, - } = Util.UIStrings; + } = detailsRenderer.strings; dom.find( '.lh-snippet__btn-label-collapse', header diff --git a/lighthouse-core/report/html/renderer/util.js b/lighthouse-core/report/html/renderer/util.js index aa16df28a58e..e6b5010c2b62 100644 --- a/lighthouse-core/report/html/renderer/util.js +++ b/lighthouse-core/report/html/renderer/util.js @@ -96,7 +96,7 @@ class Util { // Set locale for number/date formatting and grab localized renderer strings from the LHR. Util.setNumberDateLocale(clone.configSettings.locale); if (clone.i18n && clone.i18n.rendererFormattedStrings) { - Util.updateAllUIStrings(clone.i18n.rendererFormattedStrings); + Util.setDefaultUIStrings(clone.i18n.rendererFormattedStrings); } // For convenience, smoosh all AuditResults into their auditRef (which has just weight & group) @@ -125,14 +125,14 @@ class Util { return clone; } - /** + * Set missing renderer strings to default (english) values. * @param {LH.I18NRendererStrings} rendererFormattedStrings */ - static updateAllUIStrings(rendererFormattedStrings) { - // TODO(i18n): don't mutate these here but on the LHR and pass that around everywhere - for (const [key, value] of Object.entries(rendererFormattedStrings)) { - Util.UIStrings[key] = value; + static setDefaultUIStrings(rendererFormattedStrings) { + for (const [key_, value] of Object.entries(Util.UIStrings)) { + const key = /** @type {keyof typeof Util.UIStrings} */ (key_); + rendererFormattedStrings[key] = rendererFormattedStrings[key] || value; } } @@ -626,7 +626,6 @@ Util.numberFormatter = new Intl.NumberFormat(Util.numberDateLocale); /** * Report-renderer-specific strings. - * @type {LH.I18NRendererStrings} */ Util.UIStrings = { /** Disclaimer shown to users below the metric values (First Contentful Paint, Time to Interactive, etc) to warn them that the numbers they see will likely change slightly the next time they run Lighthouse. */ diff --git a/types/lhr.d.ts b/types/lhr.d.ts index 567a0ed23a9e..a03ebe29ec29 100644 --- a/types/lhr.d.ts +++ b/types/lhr.d.ts @@ -5,6 +5,9 @@ */ import LHError = require('../lighthouse-core/lib/lh-error.js'); +import Util = require('../lighthouse-core/report/html/renderer/util.js'); + +type a = keyof typeof Util['UIStrings']; declare global { module LH { @@ -17,9 +20,7 @@ declare global { [icuMessageId: string]: I18NMessageEntry[]; } - export interface I18NRendererStrings { - [varName: string]: string; - } + export type I18NRendererStrings = typeof Util['UIStrings']; export interface Environment { /** The user agent string of the version of Chrome used. */ From c9af0c57943b94c90442aa62f1051eaa70aa7609 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Mon, 30 Dec 2019 13:19:15 -0800 Subject: [PATCH 02/10] i18n class --- .../audits/dobetterweb/dom-size.js | 15 +- lighthouse-core/audits/mixed-content.js | 5 +- lighthouse-core/audits/predictive-perf.js | 6 +- lighthouse-core/lib/i18n/i18n.js | 3 +- .../report/html/html-report-assets.js | 1 + .../report/html/renderer/category-renderer.js | 14 +- .../html/renderer/crc-details-renderer.js | 10 +- .../report/html/renderer/details-renderer.js | 14 +- lighthouse-core/report/html/renderer/i18n.js | 157 ++++++++++++++++++ .../renderer/performance-category-renderer.js | 4 +- .../report/html/renderer/report-renderer.js | 11 +- .../report/html/renderer/snippet-renderer.js | 2 +- lighthouse-core/report/html/renderer/util.js | 152 ++--------------- types/html-renderer.d.ts | 3 + 14 files changed, 215 insertions(+), 182 deletions(-) create mode 100644 lighthouse-core/report/html/renderer/i18n.js diff --git a/lighthouse-core/audits/dobetterweb/dom-size.js b/lighthouse-core/audits/dobetterweb/dom-size.js index c01436bb9a68..74af579d1978 100644 --- a/lighthouse-core/audits/dobetterweb/dom-size.js +++ b/lighthouse-core/audits/dobetterweb/dom-size.js @@ -13,8 +13,8 @@ 'use strict'; const Audit = require('../audit.js'); -const Util = require('../../report/html/renderer/util.js'); -const i18n = require('../../lib/i18n/i18n.js'); +const I18n = require('../../report/html/renderer/i18n.js'); +const i18n_ = require('../../lib/i18n/i18n.js'); const UIStrings = { /** Title of a diagnostic audit that provides detail on the size of the web page's DOM. The size of a DOM is characterized by the total number of DOM elements and greatest DOM depth. This descriptive title is shown to users when the amount is acceptable and no user action is required. */ @@ -44,8 +44,7 @@ const UIStrings = { statisticDOMWidth: 'Maximum Child Elements', }; -const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings); - +const str_ = i18n_.createMessageInstanceIdFn(__filename, UIStrings); class DOMSize extends Audit { /** @@ -97,12 +96,14 @@ class DOMSize extends Audit { {key: 'value', itemType: 'numeric', text: str_(UIStrings.columnValue)}, ]; + const i18n = new I18n(context.settings.locale); + /** @type {LH.Audit.Details.Table['items']} */ const items = [ { statistic: str_(UIStrings.statisticDOMElements), element: '', - value: Util.formatNumber(stats.totalBodyElements), + value: i18n.formatNumber(stats.totalBodyElements), }, { statistic: str_(UIStrings.statisticDOMDepth), @@ -110,7 +111,7 @@ class DOMSize extends Audit { type: 'code', value: stats.depth.snippet, }, - value: Util.formatNumber(stats.depth.max), + value: i18n.formatNumber(stats.depth.max), }, { statistic: str_(UIStrings.statisticDOMWidth), @@ -118,7 +119,7 @@ class DOMSize extends Audit { type: 'code', value: stats.width.snippet, }, - value: Util.formatNumber(stats.width.max), + value: i18n.formatNumber(stats.width.max), }, ]; diff --git a/lighthouse-core/audits/mixed-content.js b/lighthouse-core/audits/mixed-content.js index 252f9315c2cf..5e3ec40d3ee2 100644 --- a/lighthouse-core/audits/mixed-content.js +++ b/lighthouse-core/audits/mixed-content.js @@ -7,7 +7,7 @@ const Audit = require('./audit.js'); const URL = require('../lib/url-shim.js'); -const Util = require('../report/html/renderer/util.js'); +const I18n = require('../report/html/renderer/i18n.js'); const NetworkRecords = require('../computed/network-records.js'); /** @@ -126,7 +126,8 @@ class MixedContent extends Audit { upgradeableResources.push(resource); } - const displayValue = `${Util.formatNumber(upgradeableResources.length)} + const i18n = new I18n(context.settings.locale); + const displayValue = `${i18n.formatNumber(upgradeableResources.length)} ${upgradeableResources.length === 1 ? 'request' : 'requests'}`; /** @type {LH.Audit.Details.Table['headings']} */ diff --git a/lighthouse-core/audits/predictive-perf.js b/lighthouse-core/audits/predictive-perf.js index c96cc23098c4..74ca4444e002 100644 --- a/lighthouse-core/audits/predictive-perf.js +++ b/lighthouse-core/audits/predictive-perf.js @@ -6,7 +6,7 @@ 'use strict'; const Audit = require('./audit.js'); -const Util = require('../report/html/renderer/util.js'); +const I18n = require('../report/html/renderer/i18n.js'); const LanternFcp = require('../computed/metrics/lantern-first-contentful-paint.js'); const LanternFmp = require('../computed/metrics/lantern-first-meaningful-paint.js'); @@ -92,10 +92,12 @@ class PredictivePerf extends Audit { SCORING_MEDIAN ); + const i18n = new I18n(context.settings.locale); + return { score, numericValue: values.roughEstimateOfTTI, - displayValue: Util.formatMilliseconds(values.roughEstimateOfTTI), + displayValue: i18n.formatMilliseconds(values.roughEstimateOfTTI), details: { type: 'debugdata', // TODO: Consider not nesting values under `items`. diff --git a/lighthouse-core/lib/i18n/i18n.js b/lighthouse-core/lib/i18n/i18n.js index b697bf86a523..fbc84c926a01 100644 --- a/lighthouse-core/lib/i18n/i18n.js +++ b/lighthouse-core/lib/i18n/i18n.js @@ -337,7 +337,7 @@ function getRendererFormattedStrings(locale) { if (!localeMessages) throw new Error(`Unsupported locale '${locale}'`); const icuMessageIds = Object.keys(localeMessages).filter(f => f.includes('core/report/html/')); - /** @type {LH.I18NRendererStrings} */ + /** @type {Record} */ const strings = {}; for (const icuMessageId of icuMessageIds) { const [filename, varName] = icuMessageId.split(' | '); @@ -345,6 +345,7 @@ function getRendererFormattedStrings(locale) { strings[varName] = localeMessages[icuMessageId].message; } + // @ts-ignore return strings; } diff --git a/lighthouse-core/report/html/html-report-assets.js b/lighthouse-core/report/html/html-report-assets.js index 5449a81b735b..306956855ef6 100644 --- a/lighthouse-core/report/html/html-report-assets.js +++ b/lighthouse-core/report/html/html-report-assets.js @@ -24,6 +24,7 @@ const REPORT_JAVASCRIPT = [ fs.readFileSync(__dirname + '/renderer/performance-category-renderer.js', 'utf8'), fs.readFileSync(__dirname + '/renderer/pwa-category-renderer.js', 'utf8'), fs.readFileSync(__dirname + '/renderer/report-renderer.js', 'utf8'), + fs.readFileSync(__dirname + '/renderer/i18n.js', 'utf8'), ].join(';\n'); const REPORT_CSS = fs.readFileSync(__dirname + '/report-styles.css', 'utf8'); const REPORT_TEMPLATES = fs.readFileSync(__dirname + '/templates.html', 'utf8'); diff --git a/lighthouse-core/report/html/renderer/category-renderer.js b/lighthouse-core/report/html/renderer/category-renderer.js index eb20b0511c9c..cdbbaaa3c8f7 100644 --- a/lighthouse-core/report/html/renderer/category-renderer.js +++ b/lighthouse-core/report/html/renderer/category-renderer.js @@ -45,10 +45,10 @@ class CategoryRenderer { */ get _clumpTitles() { return { - warning: this.detailsRenderer.strings.warningAuditsGroupTitle, - manual: this.detailsRenderer.strings.manualAuditsGroupTitle, - passed: this.detailsRenderer.strings.passedAuditsGroupTitle, - notApplicable: this.detailsRenderer.strings.notApplicableAuditsGroupTitle, + warning: Util.i18n.strings.warningAuditsGroupTitle, + manual: Util.i18n.strings.manualAuditsGroupTitle, + passed: Util.i18n.strings.passedAuditsGroupTitle, + notApplicable: Util.i18n.strings.notApplicableAuditsGroupTitle, }; } @@ -68,7 +68,7 @@ class CategoryRenderer { * @return {Element} */ populateAuditValues(audit, tmpl) { - const strings = this.detailsRenderer.strings; + const strings = Util.i18n.strings; const auditEl = this.dom.find('.lh-audit', tmpl); auditEl.id = audit.result.id; const scoreDisplayMode = audit.result.scoreDisplayMode; @@ -289,7 +289,7 @@ class CategoryRenderer { const summaryInnerEl = this.dom.find('.lh-audit-group__summary', clumpElement); const chevronEl = summaryInnerEl.appendChild(this._createChevron()); - chevronEl.title = this.detailsRenderer.strings.auditGroupExpandTooltip; + chevronEl.title = Util.i18n.strings.auditGroupExpandTooltip; const headerEl = this.dom.find('.lh-audit-group__header', clumpElement); const title = this._clumpTitles[clumpId]; @@ -347,7 +347,7 @@ class CategoryRenderer { percentageEl.textContent = scoreOutOf100.toString(); if (category.score === null) { percentageEl.textContent = '?'; - percentageEl.title = this.detailsRenderer.strings.errorLabel; + percentageEl.title = Util.i18n.strings.errorLabel; } this.dom.find('.lh-gauge__label', tmpl).textContent = category.title; diff --git a/lighthouse-core/report/html/renderer/crc-details-renderer.js b/lighthouse-core/report/html/renderer/crc-details-renderer.js index 031be3dfc04f..3a3458afd173 100644 --- a/lighthouse-core/report/html/renderer/crc-details-renderer.js +++ b/lighthouse-core/report/html/renderer/crc-details-renderer.js @@ -129,9 +129,9 @@ class CriticalRequestChainRenderer { if (!segment.hasChildren) { const {startTime, endTime, transferSize} = segment.node.request; const span = dom.createElement('span', 'crc-node__chain-duration'); - span.textContent = ' - ' + Util.formatMilliseconds((endTime - startTime) * 1000) + ', '; + span.textContent = ' - ' + Util.i18n.formatMilliseconds((endTime - startTime) * 1000) + ', '; const span2 = dom.createElement('span', 'crc-node__chain-duration'); - span2.textContent = Util.formatBytesToKB(transferSize, 0.01); + span2.textContent = Util.i18n.formatBytesToKB(transferSize, 0.01); treevalEl.appendChild(span); treevalEl.appendChild(span2); @@ -172,11 +172,11 @@ class CriticalRequestChainRenderer { const containerEl = dom.find('.lh-crc', tmpl); // Fill in top summary. - dom.find('.crc-initial-nav', tmpl).textContent = detailsRenderer.strings.crcInitialNavigation; + dom.find('.crc-initial-nav', tmpl).textContent = Util.i18n.strings.crcInitialNavigation; dom.find('.lh-crc__longest_duration_label', tmpl).textContent = - detailsRenderer.strings.crcLongestDurationLabel; + Util.i18n.strings.crcLongestDurationLabel; dom.find('.lh-crc__longest_duration', tmpl).textContent = - Util.formatMilliseconds(details.longestChain.duration); + Util.i18n.formatMilliseconds(details.longestChain.duration); // Construct visual tree. const root = CRCRenderer.initTree(details.chains); diff --git a/lighthouse-core/report/html/renderer/details-renderer.js b/lighthouse-core/report/html/renderer/details-renderer.js index 14248c137186..54edee4af8a8 100644 --- a/lighthouse-core/report/html/renderer/details-renderer.js +++ b/lighthouse-core/report/html/renderer/details-renderer.js @@ -25,19 +25,13 @@ const URL_PREFIXES = ['http://', 'https://', 'data:']; class DetailsRenderer { /** * @param {DOM} dom - * @param {LH.ReportResult} report */ - constructor(dom, report) { + constructor(dom) { this._dom = dom; - this._report = report; /** @type {ParentNode} */ this._templateContext; // eslint-disable-line no-unused-expressions } - get strings() { - return this._report.i18n.rendererFormattedStrings; - } - /** * @param {ParentNode} context */ @@ -81,7 +75,7 @@ class DetailsRenderer { */ _renderBytes(details) { // TODO: handle displayUnit once we have something other than 'kb' - const value = Util.formatBytesToKB(details.value, details.granularity); + const value = Util.i18n.formatBytesToKB(details.value, details.granularity); return this._renderText(value); } @@ -90,9 +84,9 @@ class DetailsRenderer { * @return {Element} */ _renderMilliseconds(details) { - let value = Util.formatMilliseconds(details.value, details.granularity); + let value = Util.i18n.formatMilliseconds(details.value, details.granularity); if (details.displayUnit === 'duration') { - value = Util.formatDuration(details.value); + value = Util.i18n.formatDuration(details.value); } return this._renderText(value); diff --git a/lighthouse-core/report/html/renderer/i18n.js b/lighthouse-core/report/html/renderer/i18n.js new file mode 100644 index 000000000000..8ab22984a626 --- /dev/null +++ b/lighthouse-core/report/html/renderer/i18n.js @@ -0,0 +1,157 @@ +/** + * @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'; + +/* globals self, URL */ + +// Not named `NBSP` because that creates a duplicate identifier (util.js). +const NBSP2 = '\xa0'; + +class I18n { + /** + * @param {LH.Locale} locale + */ + constructor(locale) { + this.setNumberDateLocale(locale); + this._numberDateLocale = locale; + this._numberFormatter = new Intl.NumberFormat(locale); + } + + /** + * @param {LH.I18NRendererStrings} strings + */ + setStrings(strings) { + this._strings = strings; + } + + get strings() { + return this._strings; + } + + /** + * Format number. + * @param {number} number + * @param {number=} granularity Number of decimal places to include. Defaults to 0.1. + * @return {string} + */ + formatNumber(number, granularity = 0.1) { + const coarseValue = Math.round(number / granularity) * granularity; + return this._numberFormatter.format(coarseValue); + } + + /** + * @param {number} size + * @param {number=} granularity Controls how coarse the displayed value is, defaults to .01 + * @return {string} + */ + formatBytesToKB(size, granularity = 0.1) { + const kbs = this._numberFormatter.format(Math.round(size / 1024 / granularity) * granularity); + return `${kbs}${NBSP2}KB`; + } + + /** + * @param {number} ms + * @param {number=} granularity Controls how coarse the displayed value is, defaults to 10 + * @return {string} + */ + formatMilliseconds(ms, granularity = 10) { + const coarseTime = Math.round(ms / granularity) * granularity; + return `${this._numberFormatter.format(coarseTime)}${NBSP2}ms`; + } + + /** + * @param {number} ms + * @param {number=} granularity Controls how coarse the displayed value is, defaults to 0.1 + * @return {string} + */ + formatSeconds(ms, granularity = 0.1) { + const coarseTime = Math.round(ms / 1000 / granularity) * granularity; + return `${this._numberFormatter.format(coarseTime)}${NBSP2}s`; + } + + /** + * Format time. + * @param {string} date + * @return {string} + */ + formatDateTime(date) { + /** @type {Intl.DateTimeFormatOptions} */ + const options = { + month: 'short', day: 'numeric', year: 'numeric', + hour: 'numeric', minute: 'numeric', timeZoneName: 'short', + }; + let formatter = new Intl.DateTimeFormat(this._numberDateLocale, options); + + // Force UTC if runtime timezone could not be detected. + // See https://github.com/GoogleChrome/lighthouse/issues/1056 + const tz = formatter.resolvedOptions().timeZone; + if (!tz || tz.toLowerCase() === 'etc/unknown') { + options.timeZone = 'UTC'; + formatter = new Intl.DateTimeFormat(this._numberDateLocale, options); + } + return formatter.format(new Date(date)); + } + /** + * Converts a time in milliseconds into a duration string, i.e. `1d 2h 13m 52s` + * @param {number} timeInMilliseconds + * @return {string} + */ + formatDuration(timeInMilliseconds) { + let timeInSeconds = timeInMilliseconds / 1000; + if (Math.round(timeInSeconds) === 0) { + return 'None'; + } + + /** @type {Array} */ + const parts = []; + const unitLabels = /** @type {Object} */ ({ + d: 60 * 60 * 24, + h: 60 * 60, + m: 60, + s: 1, + }); + + Object.keys(unitLabels).forEach(label => { + const unit = unitLabels[label]; + const numberOfUnits = Math.floor(timeInSeconds / unit); + if (numberOfUnits > 0) { + timeInSeconds -= numberOfUnits * unit; + parts.push(`${numberOfUnits}\xa0${label}`); + } + }); + + return parts.join(' '); + } + + /** + * Set the locale to be used for I18n's number and date formatting functions. + * @param {LH.Locale} locale + */ + setNumberDateLocale(locale) { + // When testing, use a locale with more exciting numeric formatting + if (locale === 'en-XA') locale = 'de'; + + this._numberDateLocale = locale; + this._numberFormatter = new Intl.NumberFormat(locale); + } +} + +if (typeof module !== 'undefined' && module.exports) { + module.exports = I18n; +} else { + self.I18n = I18n; +} diff --git a/lighthouse-core/report/html/renderer/performance-category-renderer.js b/lighthouse-core/report/html/renderer/performance-category-renderer.js index 99183c3bbe5b..cde391dda953 100644 --- a/lighthouse-core/report/html/renderer/performance-category-renderer.js +++ b/lighthouse-core/report/html/renderer/performance-category-renderer.js @@ -73,7 +73,7 @@ class PerformanceCategoryRenderer extends CategoryRenderer { const displayEl = this.dom.find('.lh-audit__display-text', element); const sparklineWidthPct = `${details.overallSavingsMs / scale * 100}%`; this.dom.find('.lh-sparkline__bar', element).style.width = sparklineWidthPct; - displayEl.textContent = Util.formatSeconds(details.overallSavingsMs, 0.01); + displayEl.textContent = Util.i18n.formatSeconds(details.overallSavingsMs, 0.01); // Set [title] tooltips if (audit.result.displayValue) { @@ -112,7 +112,7 @@ class PerformanceCategoryRenderer extends CategoryRenderer { * @override */ render(category, groups, environment) { - const strings = this.detailsRenderer.strings; + const strings = Util.i18n.strings; const element = this.dom.createElement('div', 'lh-category'); if (environment === 'PSI') { const gaugeEl = this.dom.createElement('div', 'lh-score__gauge'); diff --git a/lighthouse-core/report/html/renderer/report-renderer.js b/lighthouse-core/report/html/renderer/report-renderer.js index a502bf232066..2b846311e37a 100644 --- a/lighthouse-core/report/html/renderer/report-renderer.js +++ b/lighthouse-core/report/html/renderer/report-renderer.js @@ -25,7 +25,7 @@ /** @typedef {import('./dom.js')} DOM */ -/* globals self, Util, DetailsRenderer, CategoryRenderer, PerformanceCategoryRenderer, PwaCategoryRenderer */ +/* globals self, Util, DetailsRenderer, CategoryRenderer, I18n, PerformanceCategoryRenderer, PwaCategoryRenderer */ class ReportRenderer { /** @@ -98,7 +98,7 @@ class ReportRenderer { const envValues = Util.getEnvironmentDisplayValues(report.configSettings || {}); [ {name: 'URL', description: report.finalUrl}, - {name: 'Fetch time', description: Util.formatDateTime(report.fetchTime)}, + {name: 'Fetch time', description: Util.i18n.formatDateTime(report.fetchTime)}, ...envValues, {name: 'User agent (host)', description: report.userAgent}, {name: 'User agent (network)', description: report.environment && @@ -179,7 +179,12 @@ class ReportRenderer { * @return {DocumentFragment} */ _renderReport(report) { - const detailsRenderer = new DetailsRenderer(this._dom, report); + const i18n = new I18n(report.configSettings.locale); + // Set missing renderer strings to default (english) values. + i18n.setStrings({...Util.UIStrings, ...report.i18n.rendererFormattedStrings}); + Util.i18n = i18n; + + const detailsRenderer = new DetailsRenderer(this._dom); const categoryRenderer = new CategoryRenderer(this._dom, detailsRenderer); categoryRenderer.setTemplateContext(this._templateContext); diff --git a/lighthouse-core/report/html/renderer/snippet-renderer.js b/lighthouse-core/report/html/renderer/snippet-renderer.js index b618cb61656c..1ce0ad19c6fb 100644 --- a/lighthouse-core/report/html/renderer/snippet-renderer.js +++ b/lighthouse-core/report/html/renderer/snippet-renderer.js @@ -105,7 +105,7 @@ class SnippetRenderer { const { snippetCollapseButtonLabel, snippetExpandButtonLabel, - } = detailsRenderer.strings; + } = Util.i18n.strings; dom.find( '.lh-snippet__btn-label-collapse', header diff --git a/lighthouse-core/report/html/renderer/util.js b/lighthouse-core/report/html/renderer/util.js index e6b5010c2b62..0cfa21ce717d 100644 --- a/lighthouse-core/report/html/renderer/util.js +++ b/lighthouse-core/report/html/renderer/util.js @@ -93,12 +93,6 @@ class Util { } } - // Set locale for number/date formatting and grab localized renderer strings from the LHR. - Util.setNumberDateLocale(clone.configSettings.locale); - if (clone.i18n && clone.i18n.rendererFormattedStrings) { - Util.setDefaultUIStrings(clone.i18n.rendererFormattedStrings); - } - // For convenience, smoosh all AuditResults into their auditRef (which has just weight & group) if (typeof clone.categories !== 'object') throw new Error('No categories provided.'); for (const category of Object.values(clone.categories)) { @@ -125,17 +119,6 @@ class Util { return clone; } - /** - * Set missing renderer strings to default (english) values. - * @param {LH.I18NRendererStrings} rendererFormattedStrings - */ - static setDefaultUIStrings(rendererFormattedStrings) { - for (const [key_, value] of Object.entries(Util.UIStrings)) { - const key = /** @type {keyof typeof Util.UIStrings} */ (key_); - rendererFormattedStrings[key] = rendererFormattedStrings[key] || value; - } - } - /** * Used to determine if the "passed" for the purposes of showing up in the "failed" or "passed" * sections of the report. @@ -184,101 +167,6 @@ class Util { return rating; } - /** - * Format number. - * @param {number} number - * @param {number=} granularity Number of decimal places to include. Defaults to 0.1. - * @return {string} - */ - static formatNumber(number, granularity = 0.1) { - const coarseValue = Math.round(number / granularity) * granularity; - return Util.numberFormatter.format(coarseValue); - } - - /** - * @param {number} size - * @param {number=} granularity Controls how coarse the displayed value is, defaults to .01 - * @return {string} - */ - static formatBytesToKB(size, granularity = 0.1) { - const kbs = Util.numberFormatter.format(Math.round(size / 1024 / granularity) * granularity); - return `${kbs}${NBSP}KB`; - } - - /** - * @param {number} ms - * @param {number=} granularity Controls how coarse the displayed value is, defaults to 10 - * @return {string} - */ - static formatMilliseconds(ms, granularity = 10) { - const coarseTime = Math.round(ms / granularity) * granularity; - return `${Util.numberFormatter.format(coarseTime)}${NBSP}ms`; - } - - /** - * @param {number} ms - * @param {number=} granularity Controls how coarse the displayed value is, defaults to 0.1 - * @return {string} - */ - static formatSeconds(ms, granularity = 0.1) { - const coarseTime = Math.round(ms / 1000 / granularity) * granularity; - return `${Util.numberFormatter.format(coarseTime)}${NBSP}s`; - } - - /** - * Format time. - * @param {string} date - * @return {string} - */ - static formatDateTime(date) { - /** @type {Intl.DateTimeFormatOptions} */ - const options = { - month: 'short', day: 'numeric', year: 'numeric', - hour: 'numeric', minute: 'numeric', timeZoneName: 'short', - }; - let formatter = new Intl.DateTimeFormat(Util.numberDateLocale, options); - - // Force UTC if runtime timezone could not be detected. - // See https://github.com/GoogleChrome/lighthouse/issues/1056 - const tz = formatter.resolvedOptions().timeZone; - if (!tz || tz.toLowerCase() === 'etc/unknown') { - options.timeZone = 'UTC'; - formatter = new Intl.DateTimeFormat(Util.numberDateLocale, options); - } - return formatter.format(new Date(date)); - } - /** - * Converts a time in milliseconds into a duration string, i.e. `1d 2h 13m 52s` - * @param {number} timeInMilliseconds - * @return {string} - */ - static formatDuration(timeInMilliseconds) { - let timeInSeconds = timeInMilliseconds / 1000; - if (Math.round(timeInSeconds) === 0) { - return 'None'; - } - - /** @type {Array} */ - const parts = []; - const unitLabels = /** @type {Object} */ ({ - d: 60 * 60 * 24, - h: 60 * 60, - m: 60, - s: 1, - }); - - Object.keys(unitLabels).forEach(label => { - const unit = unitLabels[label]; - const numberOfUnits = Math.floor(timeInSeconds / unit); - if (numberOfUnits > 0) { - timeInSeconds -= numberOfUnits * unit; - parts.push(`${numberOfUnits}\xa0${label}`); - } - }); - - return parts.join(' '); - } - /** * Split a string by markdown code spans (enclosed in `backticks`), splitting * into segments that were enclosed in backticks (marked as `isCode === true`) @@ -517,18 +405,18 @@ class Util { break; case 'devtools': { const {cpuSlowdownMultiplier, requestLatencyMs} = throttling; - cpuThrottling = `${Util.formatNumber(cpuSlowdownMultiplier)}x slowdown (DevTools)`; - networkThrottling = `${Util.formatNumber(requestLatencyMs)}${NBSP}ms HTTP RTT, ` + - `${Util.formatNumber(throttling.downloadThroughputKbps)}${NBSP}Kbps down, ` + - `${Util.formatNumber(throttling.uploadThroughputKbps)}${NBSP}Kbps up (DevTools)`; + cpuThrottling = `${Util.i18n.formatNumber(cpuSlowdownMultiplier)}x slowdown (DevTools)`; + networkThrottling = `${Util.i18n.formatNumber(requestLatencyMs)}${NBSP}ms HTTP RTT, ` + + `${Util.i18n.formatNumber(throttling.downloadThroughputKbps)}${NBSP}Kbps down, ` + + `${Util.i18n.formatNumber(throttling.uploadThroughputKbps)}${NBSP}Kbps up (DevTools)`; summary = 'Throttled Slow 4G network'; break; } case 'simulate': { const {cpuSlowdownMultiplier, rttMs, throughputKbps} = throttling; - cpuThrottling = `${Util.formatNumber(cpuSlowdownMultiplier)}x slowdown (Simulated)`; - networkThrottling = `${Util.formatNumber(rttMs)}${NBSP}ms TCP RTT, ` + - `${Util.formatNumber(throughputKbps)}${NBSP}Kbps throughput (Simulated)`; + cpuThrottling = `${Util.i18n.formatNumber(cpuSlowdownMultiplier)}x slowdown (Simulated)`; + networkThrottling = `${Util.i18n.formatNumber(rttMs)}${NBSP}ms TCP RTT, ` + + `${Util.i18n.formatNumber(throughputKbps)}${NBSP}Kbps throughput (Simulated)`; summary = 'Simulated Slow 4G network'; break; } @@ -550,18 +438,6 @@ class Util { }; } - /** - * Set the locale to be used for Util's number and date formatting functions. - * @param {LH.Locale} locale - */ - static setNumberDateLocale(locale) { - // When testing, use a locale with more exciting numeric formatting - if (locale === 'en-XA') locale = 'de'; - - Util.numberDateLocale = locale; - Util.numberFormatter = new Intl.NumberFormat(locale); - } - /** * Returns only lines that are near a message, or the first few lines if there are * no line messages. @@ -612,17 +488,9 @@ class Util { } } -/** - * This value is updated on each run to the locale of the report - * @type {LH.Locale} - */ -Util.numberDateLocale = 'en'; - -/** - * This value stays in sync with Util.numberDateLocale. - * @type {Intl.NumberFormat} - */ -Util.numberFormatter = new Intl.NumberFormat(Util.numberDateLocale); +/** @type {I18n} */ +// @ts-ignore: Is set in report renderer. +Util.i18n = null; /** * Report-renderer-specific strings. diff --git a/types/html-renderer.d.ts b/types/html-renderer.d.ts index c7254a22ef9e..972146ee0d6d 100644 --- a/types/html-renderer.d.ts +++ b/types/html-renderer.d.ts @@ -9,6 +9,7 @@ import _CriticalRequestChainRenderer = require('../lighthouse-core/report/html/r import _SnippetRenderer = require('../lighthouse-core/report/html/renderer/snippet-renderer.js'); import _DetailsRenderer = require('../lighthouse-core/report/html/renderer/details-renderer.js'); import _DOM = require('../lighthouse-core/report/html/renderer/dom.js'); +import _I18n = require('../lighthouse-core/report/html/renderer/i18n.js'); import _PerformanceCategoryRenderer = require('../lighthouse-core/report/html/renderer/performance-category-renderer.js'); import _PwaCategoryRenderer = require('../lighthouse-core/report/html/renderer/pwa-category-renderer.js'); import _ReportRenderer = require('../lighthouse-core/report/html/renderer/report-renderer.js'); @@ -24,6 +25,7 @@ declare global { var DetailsRenderer: typeof _DetailsRenderer; var DOM: typeof _DOM; var getFilenamePrefix: typeof _FileNamer.getFilenamePrefix; + var I18n: typeof _I18n; var PerformanceCategoryRenderer: typeof _PerformanceCategoryRenderer; var PwaCategoryRenderer: typeof _PwaCategoryRenderer; var ReportRenderer: typeof _ReportRenderer; @@ -37,6 +39,7 @@ declare global { SnippetRenderer: typeof _SnippetRenderer; DetailsRenderer: typeof _DetailsRenderer; DOM: typeof _DOM; + I18n: typeof _I18n; PerformanceCategoryRenderer: typeof _PerformanceCategoryRenderer; PwaCategoryRenderer: typeof _PwaCategoryRenderer; ReportRenderer: typeof _ReportRenderer; From 4ede333c23c6df72b5f63c654d1edc2ee2859178 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Mon, 30 Dec 2019 13:23:12 -0800 Subject: [PATCH 03/10] misc --- lighthouse-core/report/html/renderer/i18n.js | 2 +- types/lhr.d.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lighthouse-core/report/html/renderer/i18n.js b/lighthouse-core/report/html/renderer/i18n.js index 8ab22984a626..e2e608d27101 100644 --- a/lighthouse-core/report/html/renderer/i18n.js +++ b/lighthouse-core/report/html/renderer/i18n.js @@ -1,6 +1,6 @@ /** * @license - * Copyright 2017 Google Inc. All Rights Reserved. + * Copyright 2019 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. diff --git a/types/lhr.d.ts b/types/lhr.d.ts index a03ebe29ec29..c6b6815435c4 100644 --- a/types/lhr.d.ts +++ b/types/lhr.d.ts @@ -7,8 +7,6 @@ import LHError = require('../lighthouse-core/lib/lh-error.js'); import Util = require('../lighthouse-core/report/html/renderer/util.js'); -type a = keyof typeof Util['UIStrings']; - declare global { module LH { export type LighthouseError = LHError; From cdf85ac88e8d6c7d609075ceebd3a8f5c9fcf997 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Mon, 30 Dec 2019 16:42:54 -0800 Subject: [PATCH 04/10] pr --- lighthouse-core/lib/i18n/i18n.js | 8 ++++---- lighthouse-core/report/html/renderer/i18n.js | 13 ++++--------- lighthouse-core/report/html/renderer/psi.js | 4 ++-- .../report/html/renderer/report-renderer.js | 10 ++++++---- .../report/html/renderer/report-ui-features.js | 2 +- 5 files changed, 17 insertions(+), 20 deletions(-) diff --git a/lighthouse-core/lib/i18n/i18n.js b/lighthouse-core/lib/i18n/i18n.js index fbc84c926a01..ecaff402a150 100644 --- a/lighthouse-core/lib/i18n/i18n.js +++ b/lighthouse-core/lib/i18n/i18n.js @@ -337,15 +337,15 @@ function getRendererFormattedStrings(locale) { if (!localeMessages) throw new Error(`Unsupported locale '${locale}'`); const icuMessageIds = Object.keys(localeMessages).filter(f => f.includes('core/report/html/')); - /** @type {Record} */ - const strings = {}; + const strings = /** @type {LH.I18NRendererStrings} */ ({}); for (const icuMessageId of icuMessageIds) { const [filename, varName] = icuMessageId.split(' | '); if (!filename.endsWith('util.js')) throw new Error(`Unexpected message: ${icuMessageId}`); - strings[varName] = localeMessages[icuMessageId].message; + + const key = /** @type {keyof LH.I18NRendererStrings} */ (varName); + strings[key] = localeMessages[icuMessageId].message; } - // @ts-ignore return strings; } diff --git a/lighthouse-core/report/html/renderer/i18n.js b/lighthouse-core/report/html/renderer/i18n.js index e2e608d27101..a10ef9a04024 100644 --- a/lighthouse-core/report/html/renderer/i18n.js +++ b/lighthouse-core/report/html/renderer/i18n.js @@ -18,24 +18,19 @@ /* globals self, URL */ -// Not named `NBSP` because that creates a duplicate identifier (util.js). +// Not named `NBSP` because that creates a duplicate identifier (util.js). const NBSP2 = '\xa0'; class I18n { /** * @param {LH.Locale} locale + * @param {LH.I18NRendererStrings=} strings */ - constructor(locale) { + constructor(locale, strings) { this.setNumberDateLocale(locale); this._numberDateLocale = locale; this._numberFormatter = new Intl.NumberFormat(locale); - } - - /** - * @param {LH.I18NRendererStrings} strings - */ - setStrings(strings) { - this._strings = strings; + this._strings = /** @type {LH.I18NRendererStrings} */ (strings || {}); } get strings() { diff --git a/lighthouse-core/report/html/renderer/psi.js b/lighthouse-core/report/html/renderer/psi.js index 8066fdfd9880..a49cadb16058 100644 --- a/lighthouse-core/report/html/renderer/psi.js +++ b/lighthouse-core/report/html/renderer/psi.js @@ -48,9 +48,9 @@ function prepareLabData(LHResult, document) { if (!reportLHR.categoryGroups) throw new Error(`No category groups found.`); // Use custom title and description. - reportLHR.categoryGroups.metrics.title = lhResult.i18n.rendererFormattedStrings.labDataTitle; + reportLHR.categoryGroups.metrics.title = Util.i18n.strings.labDataTitle; reportLHR.categoryGroups.metrics.description = - lhResult.i18n.rendererFormattedStrings.lsPerformanceCategoryDescription; + Util.i18n.strings.lsPerformanceCategoryDescription; const perfRenderer = new PerformanceCategoryRenderer(dom, new DetailsRenderer(dom)); // PSI environment string will ensure the categoryHeader and permalink elements are excluded diff --git a/lighthouse-core/report/html/renderer/report-renderer.js b/lighthouse-core/report/html/renderer/report-renderer.js index 2b846311e37a..adcc320c4897 100644 --- a/lighthouse-core/report/html/renderer/report-renderer.js +++ b/lighthouse-core/report/html/renderer/report-renderer.js @@ -130,7 +130,7 @@ class ReportRenderer { const container = this._dom.cloneTemplate('#tmpl-lh-warnings--toplevel', this._templateContext); const message = this._dom.find('.lh-warnings__msg', container); - message.textContent = report.i18n.rendererFormattedStrings.toplevelWarningsMessage; + message.textContent = Util.i18n.strings.toplevelWarningsMessage; const warnings = this._dom.find('ul', container); for (const warningString of report.runWarnings) { @@ -179,9 +179,11 @@ class ReportRenderer { * @return {DocumentFragment} */ _renderReport(report) { - const i18n = new I18n(report.configSettings.locale); - // Set missing renderer strings to default (english) values. - i18n.setStrings({...Util.UIStrings, ...report.i18n.rendererFormattedStrings}); + const i18n = new I18n(report.configSettings.locale, { + // Set missing renderer strings to default (english) values. + ...Util.UIStrings, + ...report.i18n.rendererFormattedStrings, + }); Util.i18n = i18n; const detailsRenderer = new DetailsRenderer(this._dom); diff --git a/lighthouse-core/report/html/renderer/report-ui-features.js b/lighthouse-core/report/html/renderer/report-ui-features.js index 5658602b2e64..0aa51f19af7b 100644 --- a/lighthouse-core/report/html/renderer/report-ui-features.js +++ b/lighthouse-core/report/html/renderer/report-ui-features.js @@ -252,7 +252,7 @@ class ReportUIFeatures { this._dom.find('.lh-3p-filter-count', filterTemplate).textContent = `${thirdPartyRows.size}`; this._dom.find('.lh-3p-ui-string', filterTemplate).textContent = - this.json.i18n.rendererFormattedStrings.thirdPartyResourcesLabel; + Util.i18n.strings.thirdPartyResourcesLabel; // If all or none of the rows are 3rd party, disable the checkbox. if (thirdPartyRows.size === urlItems.length || !thirdPartyRows.size) { From e4f24763c482102b4c9ce3e376eab6ecc50e0e02 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Mon, 30 Dec 2019 16:45:20 -0800 Subject: [PATCH 05/10] rm setNumberDateLocale --- lighthouse-core/report/html/renderer/i18n.js | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/lighthouse-core/report/html/renderer/i18n.js b/lighthouse-core/report/html/renderer/i18n.js index a10ef9a04024..4d953132afa1 100644 --- a/lighthouse-core/report/html/renderer/i18n.js +++ b/lighthouse-core/report/html/renderer/i18n.js @@ -27,7 +27,9 @@ class I18n { * @param {LH.I18NRendererStrings=} strings */ constructor(locale, strings) { - this.setNumberDateLocale(locale); + // When testing, use a locale with more exciting numeric formatting. + if (locale === 'en-XA') locale = 'de'; + this._numberDateLocale = locale; this._numberFormatter = new Intl.NumberFormat(locale); this._strings = /** @type {LH.I18NRendererStrings} */ (strings || {}); @@ -131,18 +133,6 @@ class I18n { return parts.join(' '); } - - /** - * Set the locale to be used for I18n's number and date formatting functions. - * @param {LH.Locale} locale - */ - setNumberDateLocale(locale) { - // When testing, use a locale with more exciting numeric formatting - if (locale === 'en-XA') locale = 'de'; - - this._numberDateLocale = locale; - this._numberFormatter = new Intl.NumberFormat(locale); - } } if (typeof module !== 'undefined' && module.exports) { From 7f5ad9e964affb80fadcbdef2bfdec06d8d76e7a Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Fri, 3 Jan 2020 16:09:22 -0800 Subject: [PATCH 06/10] fix tests --- .../html/renderer/category-renderer-test.js | 3 + .../renderer/crc-details-renderer-test.js | 3 + .../html/renderer/details-renderer-test.js | 3 + .../test/report/html/renderer/dom-test.js | 3 + .../test/report/html/renderer/i18n-test.js | 80 +++++++++++++++++++ .../performance-category-renderer-test.js | 3 + .../test/report/html/renderer/psi-test.js | 4 + .../renderer/pwa-category-renderer-test.js | 3 + .../html/renderer/report-renderer-test.js | 3 + .../html/renderer/report-ui-features-test.js | 3 + .../html/renderer/snippet-renderer-test.js | 3 + .../test/report/html/renderer/util-test.js | 76 ++---------------- 12 files changed, 116 insertions(+), 71 deletions(-) create mode 100644 lighthouse-core/test/report/html/renderer/i18n-test.js diff --git a/lighthouse-core/test/report/html/renderer/category-renderer-test.js b/lighthouse-core/test/report/html/renderer/category-renderer-test.js index b46136484d82..2da0078153df 100644 --- a/lighthouse-core/test/report/html/renderer/category-renderer-test.js +++ b/lighthouse-core/test/report/html/renderer/category-renderer-test.js @@ -11,6 +11,7 @@ const assert = require('assert'); const fs = require('fs'); const jsdom = require('jsdom'); const Util = require('../../../../report/html/renderer/util.js'); +const I18n = require('../../../../report/html/renderer/i18n.js'); const URL = require('../../../../lib/url-shim.js'); const DOM = require('../../../../report/html/renderer/dom.js'); const DetailsRenderer = require('../../../../report/html/renderer/details-renderer.js'); @@ -29,6 +30,7 @@ describe('CategoryRenderer', () => { beforeAll(() => { global.URL = URL; global.Util = Util; + global.Util.i18n = new I18n('en', {...Util.UIStrings}); global.CriticalRequestChainRenderer = CriticalRequestChainRenderer; const {document} = new jsdom.JSDOM(TEMPLATE_FILE).window; @@ -41,6 +43,7 @@ describe('CategoryRenderer', () => { afterAll(() => { global.URL = undefined; + global.Util.i18n = undefined; global.Util = undefined; global.CriticalRequestChainRenderer = undefined; }); diff --git a/lighthouse-core/test/report/html/renderer/crc-details-renderer-test.js b/lighthouse-core/test/report/html/renderer/crc-details-renderer-test.js index f5d8ca14ce6a..3bad9dfcc0c7 100644 --- a/lighthouse-core/test/report/html/renderer/crc-details-renderer-test.js +++ b/lighthouse-core/test/report/html/renderer/crc-details-renderer-test.js @@ -12,6 +12,7 @@ const fs = require('fs'); const jsdom = require('jsdom'); const URL = require('../../../../lib/url-shim.js'); const Util = require('../../../../report/html/renderer/util.js'); +const I18n = require('../../../../report/html/renderer/i18n.js'); const DOM = require('../../../../report/html/renderer/dom.js'); const DetailsRenderer = require('../../../../report/html/renderer/details-renderer.js'); const CriticalRequestChainRenderer = @@ -81,6 +82,7 @@ describe('DetailsRenderer', () => { beforeAll(() => { global.URL = URL; global.Util = Util; + global.Util.i18n = new I18n('en', {...Util.UIStrings}); const {document} = new jsdom.JSDOM(TEMPLATE_FILE).window; dom = new DOM(document); detailsRenderer = new DetailsRenderer(dom); @@ -88,6 +90,7 @@ describe('DetailsRenderer', () => { afterAll(() => { global.URL = undefined; + global.Util.i18n = undefined; global.Util = undefined; }); diff --git a/lighthouse-core/test/report/html/renderer/details-renderer-test.js b/lighthouse-core/test/report/html/renderer/details-renderer-test.js index 0b4193ebd945..86b0d19708f2 100644 --- a/lighthouse-core/test/report/html/renderer/details-renderer-test.js +++ b/lighthouse-core/test/report/html/renderer/details-renderer-test.js @@ -11,6 +11,7 @@ const jsdom = require('jsdom'); const URL = require('../../../../lib/url-shim.js'); const DOM = require('../../../../report/html/renderer/dom.js'); const Util = require('../../../../report/html/renderer/util.js'); +const I18n = require('../../../../report/html/renderer/i18n.js'); const DetailsRenderer = require('../../../../report/html/renderer/details-renderer.js'); const SnippetRenderer = require('../../../../report/html/renderer/snippet-renderer.js'); const CrcDetailsRenderer = require('../../../../report/html/renderer/crc-details-renderer.js'); @@ -26,6 +27,7 @@ describe('DetailsRenderer', () => { beforeAll(() => { global.URL = URL; global.Util = Util; + global.Util.i18n = new I18n('en', {...Util.UIStrings}); global.CriticalRequestChainRenderer = CrcDetailsRenderer; global.SnippetRenderer = SnippetRenderer; const {document} = new jsdom.JSDOM(TEMPLATE_FILE).window; @@ -36,6 +38,7 @@ describe('DetailsRenderer', () => { afterAll(() => { global.URL = undefined; + global.Util.i18n = undefined; global.Util = undefined; global.CriticalRequestChainRenderer = undefined; global.SnippetRenderer = undefined; diff --git a/lighthouse-core/test/report/html/renderer/dom-test.js b/lighthouse-core/test/report/html/renderer/dom-test.js index 63fc457c39be..a2eead0a6fb2 100644 --- a/lighthouse-core/test/report/html/renderer/dom-test.js +++ b/lighthouse-core/test/report/html/renderer/dom-test.js @@ -11,6 +11,7 @@ const jsdom = require('jsdom'); const URL = require('../../../../lib/url-shim.js'); const DOM = require('../../../../report/html/renderer/dom.js'); const Util = require('../../../../report/html/renderer/util.js'); +const I18n = require('../../../../report/html/renderer/i18n.js'); const TEMPLATE_FILE = fs.readFileSync(__dirname + '/../../../../report/html/templates.html', 'utf8'); @@ -23,6 +24,7 @@ describe('DOM', () => { beforeAll(() => { global.URL = URL; global.Util = Util; + global.Util.i18n = new I18n('en', {...Util.UIStrings}); const {document} = new jsdom.JSDOM(TEMPLATE_FILE).window; dom = new DOM(document); dom.setLighthouseChannel('someChannel'); @@ -30,6 +32,7 @@ describe('DOM', () => { afterAll(() => { global.URL = undefined; + global.Util.i18n = undefined; global.Util = undefined; }); diff --git a/lighthouse-core/test/report/html/renderer/i18n-test.js b/lighthouse-core/test/report/html/renderer/i18n-test.js new file mode 100644 index 000000000000..c38983931f63 --- /dev/null +++ b/lighthouse-core/test/report/html/renderer/i18n-test.js @@ -0,0 +1,80 @@ +/** + * @license Copyright 2019 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 Util = require('../../../../report/html/renderer/util.js'); +const I18n = require('../../../../report/html/renderer/i18n.js'); + +// Require i18n to make sure Intl is polyfilled in Node without full-icu for testing. +// When Util is run in a browser, Intl will be supplied natively (IE11+). +// eslint-disable-next-line no-unused-vars +const i18n = require('../../../../lib/i18n/i18n.js'); + +const NBSP = '\xa0'; + +/* eslint-env jest */ + +describe('util helpers', () => { + it('formats a number', () => { + const i18n = new I18n('en', {...Util.UIStrings}); + assert.strictEqual(i18n.formatNumber(10), '10'); + assert.strictEqual(i18n.formatNumber(100.01), '100'); + assert.strictEqual(i18n.formatNumber(13000.456), '13,000.5'); + }); + + it('formats a date', () => { + const i18n = new I18n('en', {...Util.UIStrings}); + const timestamp = i18n.formatDateTime('2017-04-28T23:07:51.189Z'); + assert.ok( + timestamp.includes('Apr 27, 2017') || + timestamp.includes('Apr 28, 2017') || + timestamp.includes('Apr 29, 2017') + ); + }); + + it('formats bytes', () => { + const i18n = new I18n('en', {...Util.UIStrings}); + assert.equal(i18n.formatBytesToKB(100), `0.1${NBSP}KB`); + assert.equal(i18n.formatBytesToKB(2000), `2${NBSP}KB`); + assert.equal(i18n.formatBytesToKB(1014 * 1024), `1,014${NBSP}KB`); + }); + + it('formats ms', () => { + const i18n = new I18n('en', {...Util.UIStrings}); + assert.equal(i18n.formatMilliseconds(123), `120${NBSP}ms`); + assert.equal(i18n.formatMilliseconds(2456.5, 0.1), `2,456.5${NBSP}ms`); + }); + + it('formats a duration', () => { + const i18n = new I18n('en', {...Util.UIStrings}); + assert.equal(i18n.formatDuration(60 * 1000), `1${NBSP}m`); + assert.equal(i18n.formatDuration(60 * 60 * 1000 + 5000), `1${NBSP}h 5${NBSP}s`); + assert.equal(i18n.formatDuration(28 * 60 * 60 * 1000 + 5000), `1${NBSP}d 4${NBSP}h 5${NBSP}s`); + }); + + it('formats numbers based on locale', () => { + // Requires full-icu or Intl polyfill. + const number = 12346.858558; + + const i18n = new I18n('de', {...Util.UIStrings}); + assert.strictEqual(i18n.formatNumber(number), '12.346,9'); + assert.strictEqual(i18n.formatBytesToKB(number), `12,1${NBSP}KB`); + assert.strictEqual(i18n.formatMilliseconds(number), `12.350${NBSP}ms`); + assert.strictEqual(i18n.formatSeconds(number), `12,3${NBSP}s`); + }); + + it('uses decimal comma with en-XA test locale', () => { + // Requires full-icu or Intl polyfill. + const number = 12346.858558; + + const i18n = new I18n('en-XA', {...Util.UIStrings}); + assert.strictEqual(i18n.formatNumber(number), '12.346,9'); + assert.strictEqual(i18n.formatBytesToKB(number), `12,1${NBSP}KB`); + assert.strictEqual(i18n.formatMilliseconds(number), `12.350${NBSP}ms`); + assert.strictEqual(i18n.formatSeconds(number), `12,3${NBSP}s`); + }); +}); diff --git a/lighthouse-core/test/report/html/renderer/performance-category-renderer-test.js b/lighthouse-core/test/report/html/renderer/performance-category-renderer-test.js index 7078d1e6cb67..120b2de41019 100644 --- a/lighthouse-core/test/report/html/renderer/performance-category-renderer-test.js +++ b/lighthouse-core/test/report/html/renderer/performance-category-renderer-test.js @@ -11,6 +11,7 @@ const assert = require('assert'); const fs = require('fs'); const jsdom = require('jsdom'); const Util = require('../../../../report/html/renderer/util.js'); +const I18n = require('../../../../report/html/renderer/i18n.js'); const URL = require('../../../../lib/url-shim.js'); const DOM = require('../../../../report/html/renderer/dom.js'); const DetailsRenderer = require('../../../../report/html/renderer/details-renderer.js'); @@ -30,6 +31,7 @@ describe('PerfCategoryRenderer', () => { beforeAll(() => { global.URL = URL; global.Util = Util; + global.Util.i18n = new I18n('en', {...Util.UIStrings}); global.CriticalRequestChainRenderer = CriticalRequestChainRenderer; global.CategoryRenderer = CategoryRenderer; @@ -48,6 +50,7 @@ describe('PerfCategoryRenderer', () => { afterAll(() => { global.URL = undefined; + global.Util.i18n = undefined; global.Util = undefined; global.CriticalRequestChainRenderer = undefined; global.CategoryRenderer = undefined; diff --git a/lighthouse-core/test/report/html/renderer/psi-test.js b/lighthouse-core/test/report/html/renderer/psi-test.js index b09f7c5c5077..3644d0cceeac 100644 --- a/lighthouse-core/test/report/html/renderer/psi-test.js +++ b/lighthouse-core/test/report/html/renderer/psi-test.js @@ -13,6 +13,7 @@ const jsdom = require('jsdom'); const URL = require('../../../../lib/url-shim.js'); const prepareLabData = require('../../../../report/html/renderer/psi.js'); const Util = require('../../../../report/html/renderer/util.js'); +const I18n = require('../../../../report/html/renderer/i18n.js'); const DOM = require('../../../../report/html/renderer/dom.js'); const CategoryRenderer = require('../../../../report/html/renderer/category-renderer.js'); const DetailsRenderer = require('../../../../report/html/renderer/details-renderer.js'); @@ -37,6 +38,8 @@ describe('DOM', () => { beforeAll(() => { global.URL = URL; // COMPAT: Needed for Node < 10 global.Util = Util; + global.Util.i18n = new I18n('en', {...Util.UIStrings}); + global.DOM = DOM; global.CategoryRenderer = CategoryRenderer; global.DetailsRenderer = DetailsRenderer; @@ -53,6 +56,7 @@ describe('DOM', () => { afterAll(() => { global.URL = undefined; + global.Util.i18n = undefined; global.Util = undefined; global.DOM = undefined; global.CategoryRenderer = undefined; diff --git a/lighthouse-core/test/report/html/renderer/pwa-category-renderer-test.js b/lighthouse-core/test/report/html/renderer/pwa-category-renderer-test.js index e71a81f5d1ad..57f655b906de 100644 --- a/lighthouse-core/test/report/html/renderer/pwa-category-renderer-test.js +++ b/lighthouse-core/test/report/html/renderer/pwa-category-renderer-test.js @@ -11,6 +11,7 @@ const assert = require('assert'); const fs = require('fs'); const jsdom = require('jsdom'); const Util = require('../../../../report/html/renderer/util.js'); +const I18n = require('../../../../report/html/renderer/i18n.js'); const URL = require('../../../../lib/url-shim.js'); const DOM = require('../../../../report/html/renderer/dom.js'); const DetailsRenderer = require('../../../../report/html/renderer/details-renderer.js'); @@ -28,6 +29,7 @@ describe('PwaCategoryRenderer', () => { beforeAll(() => { global.URL = URL; global.Util = Util; + global.Util.i18n = new I18n('en', {...Util.UIStrings}); global.CategoryRenderer = CategoryRenderer; const PwaCategoryRenderer = @@ -49,6 +51,7 @@ describe('PwaCategoryRenderer', () => { afterAll(() => { global.URL = undefined; + global.Util.i18n = undefined; global.Util = undefined; global.CategoryRenderer = undefined; }); diff --git a/lighthouse-core/test/report/html/renderer/report-renderer-test.js b/lighthouse-core/test/report/html/renderer/report-renderer-test.js index 821e65aa4c03..b5a109520a43 100644 --- a/lighthouse-core/test/report/html/renderer/report-renderer-test.js +++ b/lighthouse-core/test/report/html/renderer/report-renderer-test.js @@ -11,6 +11,7 @@ const assert = require('assert'); const fs = require('fs'); const jsdom = require('jsdom'); const Util = require('../../../../report/html/renderer/util.js'); +const I18n = require('../../../../report/html/renderer/i18n.js'); const URL = require('../../../../lib/url-shim.js'); const DOM = require('../../../../report/html/renderer/dom.js'); const DetailsRenderer = require('../../../../report/html/renderer/details-renderer.js'); @@ -32,6 +33,7 @@ describe('ReportRenderer', () => { beforeAll(() => { global.URL = URL; global.Util = Util; + global.I18n = I18n; global.ReportUIFeatures = ReportUIFeatures; global.CriticalRequestChainRenderer = CriticalRequestChainRenderer; global.DetailsRenderer = DetailsRenderer; @@ -64,6 +66,7 @@ describe('ReportRenderer', () => { global.self = undefined; global.URL = undefined; global.Util = undefined; + global.I18n = undefined; global.ReportUIFeatures = undefined; global.matchMedia = undefined; global.CriticalRequestChainRenderer = undefined; diff --git a/lighthouse-core/test/report/html/renderer/report-ui-features-test.js b/lighthouse-core/test/report/html/renderer/report-ui-features-test.js index 0617e471c84e..3a761fbc3060 100644 --- a/lighthouse-core/test/report/html/renderer/report-ui-features-test.js +++ b/lighthouse-core/test/report/html/renderer/report-ui-features-test.js @@ -11,6 +11,7 @@ const assert = require('assert'); const fs = require('fs'); const jsdom = require('jsdom'); const Util = require('../../../../report/html/renderer/util.js'); +const I18n = require('../../../../report/html/renderer/i18n.js'); const DOM = require('../../../../report/html/renderer/dom.js'); const DetailsRenderer = require('../../../../report/html/renderer/details-renderer.js'); const ReportUIFeatures = require('../../../../report/html/renderer/report-ui-features.js'); @@ -45,6 +46,7 @@ describe('ReportUIFeatures', () => { beforeAll(() => { global.Util = Util; + global.I18n = I18n; global.ReportUIFeatures = ReportUIFeatures; global.CriticalRequestChainRenderer = CriticalRequestChainRenderer; global.DetailsRenderer = DetailsRenderer; @@ -91,6 +93,7 @@ describe('ReportUIFeatures', () => { afterAll(() => { global.self = undefined; global.Util = undefined; + global.I18n = undefined; global.ReportUIFeatures = undefined; global.matchMedia = undefined; global.self.matchMedia = undefined; diff --git a/lighthouse-core/test/report/html/renderer/snippet-renderer-test.js b/lighthouse-core/test/report/html/renderer/snippet-renderer-test.js index 5e242cf56294..d8b3cdbce56c 100644 --- a/lighthouse-core/test/report/html/renderer/snippet-renderer-test.js +++ b/lighthouse-core/test/report/html/renderer/snippet-renderer-test.js @@ -11,6 +11,7 @@ const assert = require('assert'); const fs = require('fs'); const jsdom = require('jsdom'); const Util = require('../../../../report/html/renderer/util.js'); +const I18n = require('../../../../report/html/renderer/i18n.js'); const DOM = require('../../../../report/html/renderer/dom.js'); const SnippetRenderer = require('../../../../report/html/renderer/snippet-renderer.js'); @@ -62,11 +63,13 @@ describe('DetailsRenderer', () => { beforeAll(() => { global.Util = Util; + global.Util.i18n = new I18n('en', {...Util.UIStrings}); const {document} = new jsdom.JSDOM(TEMPLATE_FILE).window; dom = new DOM(document); }); afterAll(() => { + global.Util.i18n = undefined; global.Util = undefined; }); diff --git a/lighthouse-core/test/report/html/renderer/util-test.js b/lighthouse-core/test/report/html/renderer/util-test.js index 19009fe1af0e..e0a3931de5a9 100644 --- a/lighthouse-core/test/report/html/renderer/util-test.js +++ b/lighthouse-core/test/report/html/renderer/util-test.js @@ -7,85 +7,19 @@ const assert = require('assert'); const Util = require('../../../../report/html/renderer/util.js'); +const I18n = require('../../../../report/html/renderer/i18n.js'); const sampleResult = require('../../../results/sample_v2.json'); -// Require i18n to make sure Intl is polyfilled in Node without full-icu for testing. -// When Util is run in a browser, Intl will be supplied natively (IE11+). -// eslint-disable-next-line no-unused-vars -const i18n = require('../../../../lib/i18n/i18n.js'); - -const NBSP = '\xa0'; - /* eslint-env jest */ /* global URL */ describe('util helpers', () => { - it('formats a number', () => { - assert.strictEqual(Util.formatNumber(10), '10'); - assert.strictEqual(Util.formatNumber(100.01), '100'); - assert.strictEqual(Util.formatNumber(13000.456), '13,000.5'); - }); - - it('formats a date', () => { - const timestamp = Util.formatDateTime('2017-04-28T23:07:51.189Z'); - assert.ok( - timestamp.includes('Apr 27, 2017') || - timestamp.includes('Apr 28, 2017') || - timestamp.includes('Apr 29, 2017') - ); - }); - - it('formats bytes', () => { - assert.equal(Util.formatBytesToKB(100), `0.1${NBSP}KB`); - assert.equal(Util.formatBytesToKB(2000), `2${NBSP}KB`); - assert.equal(Util.formatBytesToKB(1014 * 1024), `1,014${NBSP}KB`); - }); - - it('formats ms', () => { - assert.equal(Util.formatMilliseconds(123), `120${NBSP}ms`); - assert.equal(Util.formatMilliseconds(2456.5, 0.1), `2,456.5${NBSP}ms`); - }); - - it('formats a duration', () => { - assert.equal(Util.formatDuration(60 * 1000), `1${NBSP}m`); - assert.equal(Util.formatDuration(60 * 60 * 1000 + 5000), `1${NBSP}h 5${NBSP}s`); - assert.equal(Util.formatDuration(28 * 60 * 60 * 1000 + 5000), `1${NBSP}d 4${NBSP}h 5${NBSP}s`); - }); - - it('formats numbers based on locale', () => { - // Requires full-icu or Intl polyfill. - const number = 12346.858558; - - const originalLocale = Util.numberDateLocale; - Util.setNumberDateLocale('de'); - assert.strictEqual(Util.formatNumber(number), '12.346,9'); - assert.strictEqual(Util.formatBytesToKB(number), `12,1${NBSP}KB`); - assert.strictEqual(Util.formatMilliseconds(number), `12.350${NBSP}ms`); - assert.strictEqual(Util.formatSeconds(number), `12,3${NBSP}s`); - - Util.setNumberDateLocale(originalLocale); // reset locale - assert.strictEqual(Util.formatNumber(number), '12,346.9'); - assert.strictEqual(Util.formatBytesToKB(number), `12.1${NBSP}KB`); - assert.strictEqual(Util.formatMilliseconds(number), `12,350${NBSP}ms`); - assert.strictEqual(Util.formatSeconds(number), `12.3${NBSP}s`); + beforeEach(() => { + Util.i18n = new I18n('en', {...Util.UIStrings}); }); - it('uses decimal comma with en-XA test locale', () => { - // Requires full-icu or Intl polyfill. - const number = 12346.858558; - - const originalLocale = Util.numberDateLocale; - Util.setNumberDateLocale('en-XA'); - assert.strictEqual(Util.formatNumber(number), '12.346,9'); - assert.strictEqual(Util.formatBytesToKB(number), `12,1${NBSP}KB`); - assert.strictEqual(Util.formatMilliseconds(number), `12.350${NBSP}ms`); - assert.strictEqual(Util.formatSeconds(number), `12,3${NBSP}s`); - - Util.setNumberDateLocale(originalLocale); // reset locale - assert.strictEqual(Util.formatNumber(number), '12,346.9'); - assert.strictEqual(Util.formatBytesToKB(number), `12.1${NBSP}KB`); - assert.strictEqual(Util.formatMilliseconds(number), `12,350${NBSP}ms`); - assert.strictEqual(Util.formatSeconds(number), `12.3${NBSP}s`); + afterEach(() => { + Util.i18n = undefined; }); it('calculates a score ratings', () => { From 2a3b7a7e8af85aeed42ada04573381c763d2dd89 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Fri, 3 Jan 2020 16:18:39 -0800 Subject: [PATCH 07/10] fix more tests --- lighthouse-core/test/audits/dobetterweb/dom-size-test.js | 7 ++++--- lighthouse-core/test/audits/mixed-content-test.js | 4 ++-- lighthouse-core/test/audits/predictive-perf-test.js | 3 ++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lighthouse-core/test/audits/dobetterweb/dom-size-test.js b/lighthouse-core/test/audits/dobetterweb/dom-size-test.js index 67c7bb457be5..3279051654e9 100644 --- a/lighthouse-core/test/audits/dobetterweb/dom-size-test.js +++ b/lighthouse-core/test/audits/dobetterweb/dom-size-test.js @@ -20,9 +20,10 @@ describe('DOMSize audit', () => { width: {max: 2, pathToElement: ['html', 'body']}, }, }; + const context = {options, settings: {locale: 'en'}}; it('calculates score hitting mid distribution', () => { - const auditResult = DOMSize.audit(artifact, {options}); + const auditResult = DOMSize.audit(artifact, context); assert.equal(auditResult.score, 0.43); assert.equal(auditResult.numericValue, numElements); expect(auditResult.displayValue).toBeDisplayString('1,500 elements'); @@ -33,11 +34,11 @@ describe('DOMSize audit', () => { it('calculates score hitting top distribution', () => { artifact.DOMStats.totalBodyElements = 400; - assert.equal(DOMSize.audit(artifact, {options}).score, 1); + assert.equal(DOMSize.audit(artifact, context).score, 1); }); it('calculates score hitting bottom of distribution', () => { artifact.DOMStats.totalBodyElements = 5970; - assert.equal(DOMSize.audit(artifact, {options}).score, 0); + assert.equal(DOMSize.audit(artifact, context).score, 0); }); }); diff --git a/lighthouse-core/test/audits/mixed-content-test.js b/lighthouse-core/test/audits/mixed-content-test.js index 7067de2a8ec9..425ecb66fabd 100644 --- a/lighthouse-core/test/audits/mixed-content-test.js +++ b/lighthouse-core/test/audits/mixed-content-test.js @@ -36,7 +36,7 @@ describe('Mixed Content audit', () => { {url: 'https://example.org/resource1.js', isSecure: true, finished: true, documentURL: 'https://example.org'}, {url: 'https://third-party.example.com/resource2.js', isSecure: true, finished: true, documentURL: 'https://example.org'}, ]; - const context = {computedCache: new Map()}; + const context = {computedCache: new Map(), settings: {locale: 'en'}}; return Audit.audit( getArtifacts('https://example.org', defaultRecords, upgradeRecords), context ).then(result => { @@ -57,7 +57,7 @@ describe('Mixed Content audit', () => { {url: 'https://third-party.example.com/resource2.js', isSecure: true, finished: true, documentURL: 'https://example.org'}, {url: 'https://fourth-party.example.com/resource3.js', isSecure: true, finished: true, documentURL: 'https://third-party.example.com'}, ]; - const context = {computedCache: new Map()}; + const context = {computedCache: new Map(), settings: {locale: 'en'}}; return Audit.audit( getArtifacts('http://example.org', defaultRecords, upgradeRecords), context ).then(result => { diff --git a/lighthouse-core/test/audits/predictive-perf-test.js b/lighthouse-core/test/audits/predictive-perf-test.js index c4a93a5cd716..08e2b590ae20 100644 --- a/lighthouse-core/test/audits/predictive-perf-test.js +++ b/lighthouse-core/test/audits/predictive-perf-test.js @@ -21,8 +21,9 @@ describe('Performance: predictive performance audit', () => { [PredictivePerf.DEFAULT_PASS]: acceptableDevToolsLog, }, }; + const context = {computedCache: new Map(), settings: {locale: 'en'}}; - return PredictivePerf.audit(artifacts, {computedCache: new Map()}).then(output => { + return PredictivePerf.audit(artifacts, context).then(output => { const metrics = output.details.items[0]; for (const [key, value] of Object.entries(metrics)) { metrics[key] = Math.round(value); From e545bf14b6776d976acdba38d660d89ade80fe5b Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Fri, 3 Jan 2020 17:34:35 -0800 Subject: [PATCH 08/10] 2020 --- lighthouse-core/report/html/renderer/i18n.js | 2 +- lighthouse-core/test/report/html/renderer/i18n-test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lighthouse-core/report/html/renderer/i18n.js b/lighthouse-core/report/html/renderer/i18n.js index 4d953132afa1..20c077387393 100644 --- a/lighthouse-core/report/html/renderer/i18n.js +++ b/lighthouse-core/report/html/renderer/i18n.js @@ -1,6 +1,6 @@ /** * @license - * Copyright 2019 Google Inc. All Rights Reserved. + * Copyright 2020 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. diff --git a/lighthouse-core/test/report/html/renderer/i18n-test.js b/lighthouse-core/test/report/html/renderer/i18n-test.js index c38983931f63..5870ea05fe25 100644 --- a/lighthouse-core/test/report/html/renderer/i18n-test.js +++ b/lighthouse-core/test/report/html/renderer/i18n-test.js @@ -1,5 +1,5 @@ /** - * @license Copyright 2019 Google Inc. All Rights Reserved. + * @license Copyright 2020 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. */ From b4b8eeddf5a5d91209a55a391765459c8646e8f7 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Mon, 6 Jan 2020 16:18:59 -0800 Subject: [PATCH 09/10] Apply suggestions from code review Co-Authored-By: Shane Exterkamp --- lighthouse-core/report/html/renderer/i18n.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lighthouse-core/report/html/renderer/i18n.js b/lighthouse-core/report/html/renderer/i18n.js index 20c077387393..367782ec71e6 100644 --- a/lighthouse-core/report/html/renderer/i18n.js +++ b/lighthouse-core/report/html/renderer/i18n.js @@ -52,7 +52,7 @@ class I18n { /** * @param {number} size - * @param {number=} granularity Controls how coarse the displayed value is, defaults to .01 + * @param {number=} granularity Controls how coarse the displayed value is, defaults to 0.1 * @return {string} */ formatBytesToKB(size, granularity = 0.1) { From 93e6794de104abd38c562c0e396f290a9b8d56cf Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Tue, 7 Jan 2020 11:07:46 -0800 Subject: [PATCH 10/10] pr --- .../report/html/renderer/category-renderer.js | 3 +-- lighthouse-core/report/html/renderer/i18n.js | 17 +++-------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/lighthouse-core/report/html/renderer/category-renderer.js b/lighthouse-core/report/html/renderer/category-renderer.js index cdbbaaa3c8f7..fa85573726de 100644 --- a/lighthouse-core/report/html/renderer/category-renderer.js +++ b/lighthouse-core/report/html/renderer/category-renderer.js @@ -119,8 +119,7 @@ class CategoryRenderer { textEl.textContent = strings.errorLabel; textEl.classList.add('tooltip-boundary'); const tooltip = this.dom.createChildOf(textEl, 'div', 'tooltip tooltip--error'); - tooltip.textContent = - audit.result.errorMessage || strings.errorMissingAuditInfo; + tooltip.textContent = audit.result.errorMessage || strings.errorMissingAuditInfo; } else if (audit.result.explanation) { const explEl = this.dom.createChildOf(titleEl, 'div', 'lh-audit-explanation'); explEl.textContent = audit.result.explanation; diff --git a/lighthouse-core/report/html/renderer/i18n.js b/lighthouse-core/report/html/renderer/i18n.js index 367782ec71e6..36532f5b9cfb 100644 --- a/lighthouse-core/report/html/renderer/i18n.js +++ b/lighthouse-core/report/html/renderer/i18n.js @@ -1,18 +1,7 @@ /** - * @license - * Copyright 2020 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. + * @license Copyright 2020 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';