-
Notifications
You must be signed in to change notification settings - Fork 9.3k
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
Add TTI < 10s audit for PWA #1840
Conversation
if (!isFast) { | ||
return LoadFastEnough4Pwa.generateAuditResult({ | ||
rawValue: false, | ||
debugString: `Under mobile conditions, the TTI was at ${ttiResult.displayValue}. ` + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need a debug string here?
also feels a little weird we're asserting here that everything was under mobile conditions when we don't really know it was :) thoughts on adding some fancy check for network throughput AND TTI matching certain conditions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very good point. I'd hate for people to setup a CI, leave off emulation, and always pass this audit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there be a unit after ${ttiResult.displayValue}
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this idea. SG.
|
||
return FastPWAAudit.audit(generateArtifactsWithTrace(traceEvents)).then(result => { | ||
assert.equal(result.rawValue, false, 'not failing a long TTI value'); | ||
TTIAudit.audit = origTTI; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will this mess anything else up if it fails? should we restore original method in catch too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was thinking about that! Maybe move to before each etc..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
another reason why we need randomized test order :)
if (!isFast) { | ||
return LoadFastEnough4Pwa.generateAuditResult({ | ||
rawValue: false, | ||
debugString: `Under mobile conditions, the TTI was at ${ttiResult.displayValue}. ` + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there be a unit after ${ttiResult.displayValue}
?
return LoadFastEnough4Pwa.generateAuditResult({ | ||
rawValue: false, | ||
debugString: `Under mobile conditions, the TTI was at ${ttiResult.displayValue}. ` + | ||
'More details in "Performance" section.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fancy link? More details in ["Performance" section](#performance).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can't do markdown in debugText. can only do it in helpText. :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and displayValue already has a unit in it. :)
@@ -0,0 +1,62 @@ | |||
/** | |||
* @license Copyright 2017 Google Inc. All Rights Reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is hideous
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but for reals, I think we should have one style or the other, and not mix them based on who wrote the file :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i am very happy to change the codebase in one full sweep.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i am very happy to change the codebase in one full sweep.
sounds perfect for another PR :P
|
||
return FastPWAAudit.audit(generateArtifactsWithTrace(traceEvents)).then(result => { | ||
assert.equal(result.rawValue, false, 'not failing a long TTI value'); | ||
TTIAudit.audit = origTTI; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
another reason why we need randomized test order :)
// https://developers.google.com/web/progressive-web-apps/checklist | ||
const MAXIMUM_TTI = 10 * 1000; | ||
|
||
class LoadFastEnough4Pwa extends Audit { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also hideous :P
'use strict'; | ||
|
||
const Audit = require('./audit'); | ||
const TTIMetric = require('./time-to-interactive'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should really just make a "page timing" computed artifact and move both FMP and TTI in there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I have need for it now too in OffscreenImages thanks to @brendankenny obnoxiously useful suggestion :P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH i think audits depending on audits is almost the same as computed artifacts depending on eachother.
and i'm kinda less interested in making one big "page timing" one, as it'd have to run TTI tracing model stuff even if you only want FMP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but now don't we have to run the TTI tracing model stuff 3x instead of just once?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gotta rerun parts of it, but the tracing model is a comptued artifact.
i profiled a run and reruning the TTI.audit() probably is like 3ms max, but we have like 6000ms of main thread time total.
const mockTTIResult = {extendedInfo: {value: {timings: {timeToInteractive: 15000}}}}; | ||
TTIAudit.audit = _ => Promise.resolve(mockTTIResult); | ||
|
||
return FastPWAAudit.audit(generateArtifactsWithTrace(traceEvents)).then(result => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not just pass a trace in? If there was a trace with one really really long top level event it should fail the test :)
}); | ||
}); | ||
|
||
it('fails a bad TTI value', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add a quick test for the network case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
quick you say..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added!
category: 'PWA', | ||
name: 'load-fast-enough-for-pwa', | ||
description: 'Page load is fast enough on 3G', | ||
helpText: 'Satisfied if the _Time To Interactive_ duration is shorter than _10 seconds_, as defined by the [PWA Baseline Checklist](https://developers.google.com/web/progressive-web-apps/checklist).', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe add a note here too that emulation must be enabled?
@@ -0,0 +1,62 @@ | |||
/** | |||
* @license Copyright 2017 Google Inc. All Rights Reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but for reals, I think we should have one style or the other, and not mix them based on who wrote the file :)
static audit(artifacts) { | ||
const networkRecords = artifacts.networkRecords[Audit.DEFAULT_PASS]; | ||
const allRequestLatencies = networkRecords.map(record => { | ||
if (!record._timing) return Infinity; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why default with Infinity
and not e.g. 0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because if there is no timing data on a request then we don't care about it. we don't want to consider it for this analysis.
i'm also happy to filter netRecords to only the ones with timing first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or alternatively the allRequestLatencies.every(val => val > latency3gMin)
thing will return true if val
is undefined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
definitely don't want to depend on implicit NaN
behavior. I was just wondering, but filtering does sound like it might be clearer (plus Infinity
doesn't make it through JSON.stringify()
, so it makes sense if including the latencies in the extendedInfo
)
|
||
const extendedInfo = { | ||
formatter: Formatter.SUPPORTED_FORMATS.NULL, | ||
value: {allRequestLatencies, timeToInteractive} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we care about having either one of these in the extended info? timeToInteractive
may make sense, I guess (e.g. if you run the PWA checks but not the perf ones), but probably not the latencies
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shrugs.
i am just thinking that people will probably want to debug failures here ( in case throttling messed up or they are trying to use diff throttling or real cellular )...
and if we know that A BUNCH of their reqs were slow enough but 1 had a latency that barely missed things, then we can change our policy here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i am just thinking that people will probably want to debug failures here ( in case throttling messed up or they are trying to use diff throttling or real cellular )...
and if we know that A BUNCH of their reqs were slow enough but 1 had a latency that barely missed things, then we can change our policy here.
OTOH things only get added to the results object, no one really gets around to trimming unused data out of extendedInfo
, but fair enough :P
}); | ||
|
||
const latency3gMin = Emulation.settings.TYPICAL_MOBILE_THROTTLING_METRICS.latency - 10; | ||
const areLatenciesAll3G = allRequestLatencies.every(val => val > latency3gMin); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you don't include areLatenciesAll3G
in the extendedInfo
, maybe pull this all into a areLatenciesAll3G(networkRecords)
function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
incidentally, this is a really cool way of checking that it's a sufficiently slow connection :)
PTAL. updated. |
@ebidel @brendankenny can i get an amen? |
@@ -0,0 +1,62 @@ | |||
/** | |||
* @license Copyright 2017 Google Inc. All Rights Reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i am very happy to change the codebase in one full sweep.
sounds perfect for another PR :P
*/ | ||
|
||
const Audit = require('./audit'); | ||
const TTIMetric = require('./time-to-interactive'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
calling into another audit really is an architecture smell, but we can fix that in a follow up
static audit(artifacts) { | ||
const networkRecords = artifacts.networkRecords[Audit.DEFAULT_PASS]; | ||
const allRequestLatencies = networkRecords.map(record => { | ||
if (!record._timing) return Infinity; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
definitely don't want to depend on implicit NaN
behavior. I was just wondering, but filtering does sound like it might be clearer (plus Infinity
doesn't make it through JSON.stringify()
, so it makes sense if including the latencies in the extendedInfo
)
TTIAudit.audit = generateTTIResults(5000); | ||
return FastPWAAudit.audit(generateArtifacts()).then(result => { | ||
assert.equal(result.rawValue, true, 'fixture trace is not passing audit'); | ||
}).catch(err => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't need catch
block (here and below), test will fail if a rejected promise is returned
|
||
const extendedInfo = { | ||
formatter: Formatter.SUPPORTED_FORMATS.NULL, | ||
value: {allRequestLatencies, timeToInteractive} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i am just thinking that people will probably want to debug failures here ( in case throttling messed up or they are trying to use diff throttling or real cellular )...
and if we know that A BUNCH of their reqs were slow enough but 1 had a latency that barely missed things, then we can change our policy here.
OTOH things only get added to the results object, no one really gets around to trimming unused data out of extendedInfo
, but fair enough :P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amen from me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🏗️🏁⏱ LGTM
Basic audit that just checks TTI value against a threshold.
Current UI is
![image](https://cloud.githubusercontent.com/assets/39191/23730226/3832a74a-041b-11e7-97ad-7f29d398957a.png)
Note: I have this audit included in default.json here, but I will remove it before landing this PR. It's just there to make testing a bit easier. :)
Once landed, I will move the other perf metrics from PWA to Perf, leaving only this one in PWA.
Fixes #1831