From e0958a8513b6be2ba15472ea7325293ee8a13b2f Mon Sep 17 00:00:00 2001 From: Brendan Kenny Date: Wed, 7 Nov 2018 15:50:38 -0800 Subject: [PATCH] report(pwa): give badges to groups with all passing audits --- lighthouse-core/config/default-config.js | 2 +- .../html/renderer/pwa-category-renderer.js | 43 +++++++++- lighthouse-core/report/html/report-styles.css | 13 +++ .../renderer/pwa-category-renderer-test.js | 85 ++++++++++++++++++- .../html/renderer/report-renderer-test.js | 15 +--- .../html/renderer/report-ui-features-test.js | 15 +--- 6 files changed, 147 insertions(+), 26 deletions(-) diff --git a/lighthouse-core/config/default-config.js b/lighthouse-core/config/default-config.js index 6fa2b307617a..39ad7100f57c 100644 --- a/lighthouse-core/config/default-config.js +++ b/lighthouse-core/config/default-config.js @@ -372,7 +372,7 @@ const defaultConfig = { {id: 'is-on-https', weight: 2, group: 'pwa-installable'}, {id: 'service-worker', weight: 1, group: 'pwa-installable'}, {id: 'webapp-install-banner', weight: 2, group: 'pwa-installable'}, - // Engaging + // PWA Optimized {id: 'redirects-http', weight: 2, group: 'pwa-optimized'}, {id: 'splash-screen', weight: 1, group: 'pwa-optimized'}, {id: 'themed-omnibox', weight: 1, group: 'pwa-optimized'}, diff --git a/lighthouse-core/report/html/renderer/pwa-category-renderer.js b/lighthouse-core/report/html/renderer/pwa-category-renderer.js index 72d3a6fd5143..57a5710b2cc4 100644 --- a/lighthouse-core/report/html/renderer/pwa-category-renderer.js +++ b/lighthouse-core/report/html/renderer/pwa-category-renderer.js @@ -16,7 +16,7 @@ */ 'use strict'; -/* globals self, CategoryRenderer */ +/* globals self, Util, CategoryRenderer */ class PwaCategoryRenderer extends CategoryRenderer { /** @@ -34,7 +34,7 @@ class PwaCategoryRenderer extends CategoryRenderer { // Regular audits aren't split up into pass/fail/not-applicable clumps, they're // all put in a top-level clump that isn't expandable/collapsable. const regularAuditRefs = auditRefs.filter(ref => ref.result.scoreDisplayMode !== 'manual'); - const auditsElem = this.renderUnexpandableClump(regularAuditRefs, groupDefinitions); + const auditsElem = this._renderAudits(regularAuditRefs, groupDefinitions); categoryElem.appendChild(auditsElem); // Manual audits are still in a manual clump. @@ -45,6 +45,45 @@ class PwaCategoryRenderer extends CategoryRenderer { return categoryElem; } + + /** + * Returns the group IDs whose audits are all considered passing. + * @param {Array} auditRefs + * @return {Set} + */ + _getPassingGroupIds(auditRefs) { + const groupIds = auditRefs.map(ref => ref.group).filter(/** @return {g is string} */ g => !!g); + const uniqueGroupIds = new Set(groupIds); + + // Remove any that have a failing audit. + for (const auditRef of auditRefs) { + if (!Util.showAsPassed(auditRef.result) && auditRef.group) { + uniqueGroupIds.delete(auditRef.group); + } + } + + return uniqueGroupIds; + } + + /** + * Render non-manual audits in groups, giving a badge to any group that has + * all passing audits. + * @param {Array} auditRefs + * @param {Object} groupDefinitions + * @return {Element} + */ + _renderAudits(auditRefs, groupDefinitions) { + const auditsElem = this.renderUnexpandableClump(auditRefs, groupDefinitions); + + // Add a 'badged' class to group if all audits in that group pass. + const passsingGroupIds = this._getPassingGroupIds(auditRefs); + for (const groupId of passsingGroupIds) { + const groupElem = this.dom.find(`.lh-audit-group--${groupId}`, auditsElem); + groupElem.classList.add('lh-badged'); + } + + return auditsElem; + } } if (typeof module !== 'undefined' && module.exports) { diff --git a/lighthouse-core/report/html/report-styles.css b/lighthouse-core/report/html/report-styles.css index eb3eddc616fb..c9723c168849 100644 --- a/lighthouse-core/report/html/report-styles.css +++ b/lighthouse-core/report/html/report-styles.css @@ -97,6 +97,10 @@ --pwa-fast-reliable-gray-url: url('data:image/svg+xml;utf8,'); --pwa-installable-gray-url: url('data:image/svg+xml;utf8,'); --pwa-optimized-gray-url: url('data:image/svg+xml;utf8,'); + + --pwa-fast-reliable-color-url: url('data:image/svg+xml;utf8,'); + --pwa-installable-color-url: url('data:image/svg+xml;utf8,'); + --pwa-optimized-color-url: url('data:image/svg+xml;utf8,'); } .lh-vars.lh-devtools { @@ -636,6 +640,15 @@ details, summary { background-image: var(--pwa-optimized-gray-url); background-size: var(--lh-group-icon-background-size); } +.lh-audit-group--pwa-fast-reliable.lh-badged .lh-audit-group__header::before { + background-image: var(--pwa-fast-reliable-color-url); +} +.lh-audit-group--pwa-installable.lh-badged .lh-audit-group__header::before { + background-image: var(--pwa-installable-color-url); +} +.lh-audit-group--pwa-optimized.lh-badged .lh-audit-group__header::before { + background-image: var(--pwa-optimized-color-url); +} /* Removing too much whitespace */ .lh-audit-group--metrics { 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 92500c3117cd..11a12a0cd161 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 @@ -39,7 +39,12 @@ describe('PwaCategoryRenderer', () => { pwaRenderer = new PwaCategoryRenderer(dom, detailsRenderer); sampleResults = Util.prepareReportResult(sampleResultsOrig); - category = sampleResults.reportCategories.find(cat => cat.id === 'pwa'); + }); + + beforeEach(() => { + // Clone category to allow modifications. + const pwaCategory = sampleResults.reportCategories.find(cat => cat.id === 'pwa'); + category = JSON.parse(JSON.stringify(pwaCategory)); }); afterAll(() => { @@ -90,4 +95,82 @@ describe('PwaCategoryRenderer', () => { `trouble with selector '${selector}'`); }); }); + + describe('badging groups', () => { + let auditRefs; + let groupIds; + + beforeEach(() => { + auditRefs = category.auditRefs + .filter(audit => audit.result.scoreDisplayMode !== 'manual'); + + // Expect results to all be scorable. + for (const auditRef of auditRefs) { + assert.strictEqual(auditRef.result.scoreDisplayMode, 'binary'); + } + + groupIds = [...new Set(auditRefs.map(ref => ref.group))]; + }); + + it('only gives a group a badge when all the group\'s audits are passing', () => { + for (const auditRef of auditRefs) { + auditRef.result.score = 0; + } + + const targetGroupId = groupIds[2]; + assert.ok(targetGroupId); + const targetAuditRefs = auditRefs.filter(ref => ref.group === targetGroupId); + + // Try every permutation of audit scoring. + const totalPermutations = Math.pow(2, targetAuditRefs.length); + for (let i = 0; i < totalPermutations; i++) { + for (let j = 0; j < targetAuditRefs.length; j++) { + // Set as passing if jth bit in i is set. + targetAuditRefs[j].result.score = i >> j & 1; + } + + const categoryElem = pwaRenderer.render(category, sampleResults.categoryGroups); + const badgedElems = categoryElem.querySelectorAll(`.lh-badged`); + + // Only expect a badge on last permutation (all bits are set). + const expectedBadgeCount = i === totalPermutations - 1 ? 1 : 0; + assert.strictEqual(badgedElems.length, expectedBadgeCount); + } + }); + + it('renders all badges when all audits are passing', () => { + for (const auditRef of auditRefs) { + auditRef.result.score = 1; + } + + const categoryElem = pwaRenderer.render(category, sampleResults.categoryGroups); + assert.strictEqual(categoryElem.querySelectorAll('.lh-badged').length, groupIds.length); + }); + + it('renders no badges when no audit groups are passing', () => { + for (const auditRef of auditRefs) { + auditRef.result.score = 0; + } + + const categoryElem = pwaRenderer.render(category, sampleResults.categoryGroups); + assert.strictEqual(categoryElem.querySelectorAll('.lh-badged').length, 0); + }); + + it('renders all but one badge when all groups but one are passing', () => { + for (const auditRef of auditRefs) { + auditRef.result.score = 1; + } + auditRefs[0].result.score = 0; + const failingGroupId = auditRefs[0].group; + + const categoryElem = pwaRenderer.render(category, sampleResults.categoryGroups); + + for (const groupId of groupIds) { + const expectedCount = groupId === failingGroupId ? 0 : 1; + + const groupElems = categoryElem.querySelectorAll(`.lh-audit-group--${groupId}.lh-badged`); + assert.strictEqual(groupElems.length, expectedCount); + } + }); + }); }); 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 87c072f2d48b..22c3fee12199 100644 --- a/lighthouse-core/test/report/html/renderer/report-renderer-test.js +++ b/lighthouse-core/test/report/html/renderer/report-renderer-test.js @@ -16,9 +16,6 @@ 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'); const CategoryRenderer = require('../../../../report/html/renderer/category-renderer.js'); -// lazy loaded because they depend on CategoryRenderer to be available globally -let PerformanceCategoryRenderer = null; -let PwaCategoryRenderer = null; const CriticalRequestChainRenderer = require( '../../../../report/html/renderer/crc-details-renderer.js'); const ReportRenderer = require('../../../../report/html/renderer/report-renderer.js'); @@ -39,16 +36,12 @@ describe('ReportRenderer', () => { global.CriticalRequestChainRenderer = CriticalRequestChainRenderer; global.DetailsRenderer = DetailsRenderer; global.CategoryRenderer = CategoryRenderer; - if (!PerformanceCategoryRenderer) { - PerformanceCategoryRenderer = + + // lazy loaded because they depend on CategoryRenderer to be available globally + global.PerformanceCategoryRenderer = require('../../../../report/html/renderer/performance-category-renderer.js'); - } - global.PerformanceCategoryRenderer = PerformanceCategoryRenderer; - if (!PwaCategoryRenderer) { - PwaCategoryRenderer = + global.PwaCategoryRenderer = require('../../../../report/html/renderer/pwa-category-renderer.js'); - } - global.PwaCategoryRenderer = PwaCategoryRenderer; // Stub out matchMedia for Node. global.matchMedia = function() { 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 cea3db85e3e5..1b3566fca31e 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 @@ -16,9 +16,6 @@ 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'); const CategoryRenderer = require('../../../../report/html/renderer/category-renderer.js'); -// lazy loaded because they depend on CategoryRenderer to be available globally -let PerformanceCategoryRenderer = null; -let PwaCategoryRenderer = null; const CriticalRequestChainRenderer = require( '../../../../report/html/renderer/crc-details-renderer.js'); const ReportRenderer = require('../../../../report/html/renderer/report-renderer.js'); @@ -41,16 +38,12 @@ describe('ReportUIFeatures', () => { global.CriticalRequestChainRenderer = CriticalRequestChainRenderer; global.DetailsRenderer = DetailsRenderer; global.CategoryRenderer = CategoryRenderer; - if (!PerformanceCategoryRenderer) { - PerformanceCategoryRenderer = + + // lazy loaded because they depend on CategoryRenderer to be available globally + global.PerformanceCategoryRenderer = require('../../../../report/html/renderer/performance-category-renderer.js'); - } - global.PerformanceCategoryRenderer = PerformanceCategoryRenderer; - if (!PwaCategoryRenderer) { - PwaCategoryRenderer = + global.PwaCategoryRenderer = require('../../../../report/html/renderer/pwa-category-renderer.js'); - } - global.PwaCategoryRenderer = PwaCategoryRenderer; // Stub out matchMedia for Node. global.matchMedia = function() {