Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core(lhr): overhaul LHR details, introduce details.summary #4616

Merged
merged 4 commits into from
Mar 9, 2018

Conversation

paulirish
Copy link
Member

@paulirish paulirish commented Feb 23, 2018

Alright, #4384 laid the groundwork here and I've built off that for round two in the goal of "making the juicy details in LHR easy to consume."

(This is a super-breaking change. Yes.)

Changes

  • completely new shape of details:
    • result.details.headings describes the expected report columns
    • result.details.items contains row information, and may include more data than referenced by the headings. it's the new extendedInfo. :)
  • a new result.details.summary obj that includes whatever toplevel numeric summary we have. So far just wastedMs and wastedKb, but I could see displayValue migrating into it.

I think this is easiest to evaluate by looking at the LHR json. Here's one audit result, before and after:

🔎 Also here's a complete LHR: tinyhouse.json.txt

@patrickhulce @brendankenny Thoughts on the new LHR shape?

cc @benschwarz @denar90


This is a chain of PRs. Landing order is... 1st: newdetails (#4616). 2nd: shallowCategories (#4711). 3rd: scoring2.0 (#4690)

ref #4614

Copy link
Collaborator

@patrickhulce patrickhulce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 ❤️ love the overall direction this is headed, I'd really like to nail down what details can look like in the new world and start documenting it as we go

Is this what we currently have?

interface AuditDetails {
  summary?: AuditDetailsSummary
  items?: AuditDetailsItem[]
  headings?: AuditTableHeading[]
  // ???
}

interface AuditDetailsItem {
  [key: string]: LegacyDetails | string | number
}

interface LegacyDetails {
  type: string
  value: string | number
  granularity?: string
  displayUnit?: string
}

interface AuditDetailsSummary {
  displayValue?: LegacyDetails
  wastedMs?: number // I might prefer sticking these two into a `performanceOpportunity` object or something, but we never really came up with other uses thus far either...
  wastedKb?: number
  // ???
}

interface AuditTableHeading extends LegacyDetails {
  key: string
  text: string
}

@@ -315,7 +337,7 @@ class DetailsRenderer {
*/
_renderCode(details) {
const pre = this._dom.createElement('pre', 'lh-code');
pre.textContent = details.text;
pre.textContent = details.text || details.value; // TODO, pick a winner
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for value

return this._dom.createElement('span');
}
_renderThumbnail(details) {
// TODO: use some empty details test instead? to handle data uris?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah let's strip out all data URIs at the audit base-class, they can make the report super beefy anyway

const extendedInfo = /** @type {!PerformanceCategoryRenderer.PerfHintExtendedInfo}
*/ (audit.result.extendedInfo);
const summaryInfo = /** @type {!PerformanceCategoryRenderer.PerfHintSummaryInfo}
*/ (audit.result.summary);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be missing something, I thought we were nesting summary inside of details?

@denar90
Copy link
Contributor

denar90 commented Feb 25, 2018

Wow, big amount of work! Thanks for noticing 👍
Is there any v3 roadmap issue? I just found only 2 pr's but no doubt there will be more)

@patrickhulce
Copy link
Collaborator

@denar90 #4333 is our wishlist for v3 breaking changes :)

@paulirish
Copy link
Member Author

Is this what we currently have?

Yes, that looks mostly accurate. I'm almost done with typechecking the JSON against an iteration of these interfaces. ;)


In the case of PSI audits we have an even simpler details story:

interface AuditDetails {
  summary?: AuditDetailsSummary  // could be also be a sibling to details..
  headings?: AuditTableHeading[]
  items?: AuditDetailsItem[]
}

interface AuditDetailsSummary {
  wastedMs?: number // maybe these two within a `performanceOpportunity` object?
  wastedKb?: number
}

interface AuditTableHeading {
  key: string
  text: string
  itemType: string
  granularity?: string
  displayUnit?: string
}

interface AuditDetailsItem {
  [key: string]: string | number
}

@paulirish paulirish changed the title WIP: refactor LHR shape of details/extendedInfo core: refactor LHR shape: scriptable details, clean reportCategories, new summary Mar 2, 2018
@paulirish paulirish changed the title core: refactor LHR shape: scriptable details, clean reportCategories, new summary core: consumable LHR - scriptable details, clean reportCategories, new summary Mar 2, 2018
@paulirish
Copy link
Member Author

@patrickhulce this is ready for review.

@@ -18,7 +18,7 @@ const IGNORE_THRESHOLD_IN_PERCENT = 0.925;
const SCORING_POINT_OF_DIMINISHING_RETURNS = 4; // 4 KB
const SCORING_MEDIAN = 768; // 768 KB

class CacheHeaders extends ByteEfficiencyAudit {
class CacheHeaders {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@paulirish let's make sure this extends audit

@@ -133,11 +137,16 @@ class DetailsRenderer {
* @return {!Element}
*/
_renderLink(details) {
console.assert(('text' in details), 'link details must have .url');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like this should be 'url' in details?

for (const heading of details.headings) {
const value = /** @type {number|string|!DetailsRenderer.DetailsJSON} */ (row[heading.key]);
if (typeof value === 'undefined') {
continue; // e.g. no lineNumber in this row item
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't we still want to create a td element even if it's empty?

@@ -93,9 +93,10 @@ class Runner {
const resultsById = {};
for (const audit of runResults.auditResults) resultsById[audit.name] = audit;

let report;
let cats;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cats

can we go categories though :)

@paulirish paulirish changed the base branch from (╯°□°)╯︵┻━┻_the_strings to master March 2, 2018 23:47
@paulirish paulirish force-pushed the newdetails branch 2 times, most recently from 27f425c to c0658ec Compare March 2, 2018 23:57
@paulirish
Copy link
Member Author

updated and squashed (to resolve silly conflicts)

…w summary

* completely new shape of `details`:
  * `result.details.headings` describes the expected report columns
  * `result.details.items` contains row information, and may include _more_ data than referenced by the headings. it's the new `extendedInfo`. :)
* new `result.details.summary` obj that includes whatever toplevel numeric summary we have. So far just `wastedMs` and `wastedKb`
* `reportCategories` not contain real auditResults (aka _de-dupe all audit results_ from #4333)
  * `reportCategories.performance` keeps it's `score` prop
  * each audit's score moves from `reportCategories.performance['audit-name']` (which now just holds `weight` and `group`) over to `audits['audit-name']`
* removes `LHR.score`, the overallScore
@paulirish paulirish changed the title core: consumable LHR - scriptable details, clean reportCategories, new summary core(LHR): overhaul details, incl details.summary, shallow reportCategories Mar 7, 2018
@paulirish paulirish changed the title core(LHR): overhaul details, incl details.summary, shallow reportCategories core(lhr): overhaul details, incl details.summary, shallow reportCategories Mar 7, 2018
@paulirish
Copy link
Member Author

@patrickhulce lgty?

Copy link
Collaborator

@patrickhulce patrickhulce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I say merge away! Never a better time for the risky business than now 👍

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible to take out most of these runtime details checks and rely on the type checker(s)? e.g. at DetailsRenderer.render, if type is text, we should be certain that it's a StringDetailsJSON at that point. We can do some of this by checking the output of our audits, but some of it should fall out naturally…

also, it looks like we won’t be able to non-awkwardly be able to get read of text.text since so many other details types still include a text?

@@ -141,20 +114,35 @@ class Audit {
if (displayValue === score) {
displayValue = '';
}

// TODO: restore after initial 3.0 branching
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete?


// TODO, don't consider an auditResult's scoringMode (currently applied to all ByteEfficiency)
const scoringMode = result.scoringMode || audit.meta.scoringMode || Audit.SCORING_MODES.BINARY;
delete result.scoringMode;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add note that will be removed in next PR

@@ -7,6 +7,7 @@

const assert = require('assert');
const parseCacheControl = require('parse-cache-control');
const Audit = require('../audit');
const ByteEfficiencyAudit = require('./byte-efficiency-audit');
Copy link
Member

@brendankenny brendankenny Mar 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still need ByteEfficiencyAudit? Looks like most things might just be through Audit?

@@ -203,9 +203,10 @@ class CategoryRenderer {
const auditsGroupedByGroup = /** @type {!Object<string,
!Array<!ReportRenderer.AuditJSON>>} */ ({});
manualAudits.forEach(audit => {
const group = auditsGroupedByGroup[audit.group] || [];
const groupId = audit.group || 'ungrouped';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this doing?

* @return {!Element}
*/
_renderTextURL(text) {
const url = text.text || '';
console.assert(!('text' in text), 'url details should not have .text');
console.assert('value' in text, 'url details must have .value');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these should be checked by the type system?

@@ -133,11 +137,16 @@ class DetailsRenderer {
* @return {!Element}
*/
_renderLink(details) {
console.assert(('url' in details), 'link details must have .url');
console.assert(('text' in details), 'link details must have .text');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

* wastedBytes: (number|undefined),
* }}
*/
DetailsRenderer.DetailsSummary; // eslint-disable-line no-unused-expressions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems more specific than a just a "summary"?

@brendankenny
Copy link
Member

if we want to get really fancy (and if we do TS checking of the report renderer), we can use discriminated unions which will type check based on the value of type in a particular branch. Will need to be in an externs file, though, because closure will choke on type: "text" in the typedef

@paulirish paulirish changed the title core(lhr): overhaul details, incl details.summary, shallow reportCategories core(lhr): overhaul LHR details, introduce details.summary Mar 8, 2018
@paulirish
Copy link
Member Author

also, it looks like we won’t be able to non-awkwardly be able to get read of text.text since so many other details types still include a text?

fwiw the current shape of details objects looks like this:

detail types with 1 required field

type text with .value
type code with .value
type thumbnail w/ .value

type ms with .value
type bytes w .value
type url w .value

detail types with >1 required field

type link with .text and .url
type node with .snippet .selector .path
type card with .title .value .target .snippet


Additionally, the second block of 1-field types (ms/bytes/url) are currently never expressed in the LHR explicitly; they are all created via headings..itemType.

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@paulirish paulirish merged commit bc9efba into master Mar 9, 2018
paulirish added a commit that referenced this pull request Mar 9, 2018
- completely new shape of `details`:
  - `result.details.headings` describes the expected report columns
  - `result.details.items` contains row information, and may include _more_ data than referenced by the headings. it's the new `extendedInfo`. :)
- new `result.details.summary` obj that includes whatever toplevel numeric summary we have. So far just `wastedMs` and `wastedKb`
@paulirish paulirish deleted the newdetails branch April 27, 2018 21:51
@paulirish paulirish removed the 3.0 label Dec 18, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants