Repository: GoogleChrome/lighthouse
Pull request #6930 (2019-01-04 20:35:05)

report: expand groups within Passed Audits
This was the UX conclusion when PSI noticed the groupings in our new renderer.

paulirish: > I'd prefer this, but I believe the conclusion was that there would be no groups under passed.

Hmm I dont remember that.  but Ok. :)


---------

Here's a GIF showing (1) current master - two collapsed groups under Passed. then (2) this PR - two expanded groups under Passed. The gif doesn't show (C) current PSI - groups are intermingled under Passed.

![kapture 2019-01-04 at 12 40 32](https://user-images.githubusercontent.com/39191/50709905-3671c600-101e-11e9-8654-1feaffd8d174.gif)

Brendan thought the conclusion was option C, but I forget, tbh.
 @kusler wanna make a call?

brendankenny: yes, I remember it clearly because I was sad to lose the headings under Passed (I believe the argument was that you don't really need them at that point, since they're just a list of passed audits), and I had just finished my sweet ascii art and didn't want to change it :)
Hoten: should https://github.com/GoogleChrome/lighthouse/pull/6279 be closed?
brendankenny: proposal on top of this in #6982

Patch:
diff --git a/lighthouse-core/report/html/renderer/category-renderer.js b/lighthouse-core/report/html/renderer/category-renderer.js
index 25976cce4..5a84a5781 100644
--- a/lighthouse-core/report/html/renderer/category-renderer.js
+++ b/lighthouse-core/report/html/renderer/category-renderer.js
@@ -217,10 +217,9 @@ class CategoryRenderer {
    * array of audit and audit-group elements.
    * @param {Array<LH.ReportResult.AuditRef>} auditRefs
    * @param {Object<string, LH.Result.ReportGroup>} groupDefinitions
-   * @param {{expandable: boolean}} opts
    * @return {Array<Element>}
    */
-  _renderGroupedAudits(auditRefs, groupDefinitions, opts) {
+  _renderGroupedAudits(auditRefs, groupDefinitions) {
     // Audits grouped by their group (or under notAGroup).
     /** @type {Map<string, Array<LH.ReportResult.AuditRef>>} */
     const grouped = new Map();
@@ -252,7 +251,10 @@ class CategoryRenderer {
 
       // Push grouped audits as a group.
       const groupDef = groupDefinitions[groupId];
-      const auditGroupElem = this.renderAuditGroup(groupDef, opts);
+      // Expanded by default!
+      // 1. The 'failed' clump has all groups expanded.
+      // 2. If a clump is collapsed (passed, n/a), we want the groups within to already be expanded
+      const auditGroupElem = this.renderAuditGroup(groupDef, {expandable: false});
       for (const auditRef of groupAuditRefs) {
         auditGroupElem.appendChild(this.renderAudit(auditRef, index++));
       }
@@ -272,7 +274,7 @@ class CategoryRenderer {
    */
   renderUnexpandableClump(auditRefs, groupDefinitions) {
     const clumpElement = this.dom.createElement('div');
-    const elements = this._renderGroupedAudits(auditRefs, groupDefinitions, {expandable: false});
+    const elements = this._renderGroupedAudits(auditRefs, groupDefinitions);
     elements.forEach(elem => clumpElement.appendChild(elem));
     return clumpElement;
   }
@@ -291,7 +293,8 @@ class CategoryRenderer {
    *      ├── audit 5
    *      └── audit 6
    * clump (e.g. 'manual')
-   *   ├── …
+   *   ├── audit 1
+   *   ├── audit 2
    *   ⋮
    * @param {TopLevelClumpId} clumpId
    * @param {{auditRefs: Array<LH.ReportResult.AuditRef>, groupDefinitions: Object<string, LH.Result.ReportGroup>, description?: string}} clumpOpts
@@ -305,17 +308,15 @@ class CategoryRenderer {
       return failedElem;
     }
 
-    const expandable = true;
-    const elements = this._renderGroupedAudits(auditRefs, groupDefinitions, {expandable});
-
     const clumpInfo = this._clumpDisplayInfo[clumpId];
     // TODO: renderAuditGroup shouldn't be used to render a clump (since it *contains* audit groups).
     const groupDef = {title: clumpInfo.title, description};
-    const opts = {expandable, itemCount: auditRefs.length};
-    const clumpElem = this.renderAuditGroup(groupDef, opts);
+    const groupOpts = {expandable: true, itemCount: auditRefs.length};
+    const clumpElem = this.renderAuditGroup(groupDef, groupOpts);
     clumpElem.classList.add('lh-clump', clumpInfo.className);
 
-    elements.forEach(elem => clumpElem.appendChild(elem));
+    // For all non-failed clumps, we don't group
+    clumpElem.append(...auditRefs.map(this.renderAudit.bind(this)));
 
     return clumpElem;
   }


Test patch:
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 8be1099a4..bbb554754 100644
--- a/lighthouse-core/test/report/html/renderer/category-renderer-test.js
+++ b/lighthouse-core/test/report/html/renderer/category-renderer-test.js
@@ -227,14 +227,16 @@ describe('CategoryRenderer', () => {
       assert.equal(failedAuditGroups.length, failedAuditTags.size);
     });
 
-    it('renders the passed audits grouped by group', () => {
+    it('renders the passed audits ungrouped', () => {
       const categoryDOM = renderer.render(category, sampleResults.categoryGroups);
       const passedAudits = category.auditRefs.filter(audit =>
           audit.result.scoreDisplayMode !== 'notApplicable' && audit.result.score === 1);
-      const passedAuditTags = new Set(passedAudits.map(audit => audit.group));
 
       const passedAuditGroups = categoryDOM.querySelectorAll('.lh-clump--passed .lh-audit-group');
-      assert.equal(passedAuditGroups.length, passedAuditTags.size);
+      const passedAuditsElems = categoryDOM.querySelectorAll('.lh-clump--passed .lh-audit');
+
+      assert.equal(passedAuditGroups.length, 0);
+      assert.equal(passedAuditsElems.length, passedAudits.length);
     });
 
     it('renders all the audits', () => {
@@ -293,15 +295,20 @@ describe('CategoryRenderer', () => {
     });
 
     it('gives each group a selectable class', () => {
+      const categoryClone = JSON.parse(JSON.stringify(category));
+      // Force all results to be Failed for accurate counting of groups.
+      categoryClone.auditRefs.forEach(ref => {
+        ref.result.score = 0;
+        ref.result.scoreDisplayMode = 'binary';
+      });
       const categoryGroupIds = new Set(category.auditRefs.filter(a => a.group).map(a => a.group));
       assert.ok(categoryGroupIds.size > 6); // Ensure there's something to test.
 
-      const categoryElem = renderer.render(category, sampleResults.categoryGroups);
+      const categoryElem = renderer.render(categoryClone, sampleResults.categoryGroups);
 
       categoryGroupIds.forEach(groupId => {
         const selector = `.lh-audit-group--${groupId}`;
-        // Could be multiple results (e.g. found in both passed and failed clumps).
-        assert.ok(categoryElem.querySelectorAll(selector).length >= 1,
+        assert.equal(categoryElem.querySelectorAll(selector).length, 1,
           `could not find '${selector}'`);
       });
     });