-
Notifications
You must be signed in to change notification settings - Fork 9.4k
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
feat(redirects-audit): Adding Redirect audit (PSI Compat) #3308
feat(redirects-audit): Adding Redirect audit (PSI Compat) #3308
Conversation
nice! using the time taken for everything but the last request (minus TCP handshake if its the same domain) as wasted ms sounds good to me |
do we need a smoke test? |
Ideally yes, though we'd have to add some kind of redirect support to the test server? Alternatively, some of the PWAs for the smokehouse run on live sites were chosen because they redirect. We could expand |
if it ends up being complicated, we can do in a followup to keep the PR single-purpose |
lighthouse-core/audits/redirects.js
Outdated
displayValue: pageRedirects.length, | ||
rawValue: passed, | ||
extendedInfo: { | ||
value: pageRedirects |
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.
yah lets calculated the wasted time. we can then show this in opportunities rather than diagnostics.
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.
a few more things noted. still thinking this should move to Opportunities with a wastedMs calculation.
lighthouse-core/audits/redirects.js
Outdated
const Audit = require('./audit'); | ||
|
||
// PSI allows one redirect (http://example.com => http://m.example.com) | ||
const REDIRECT_TRESHOLD = 1; |
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.
THRESHOLD*
lighthouse-core/audits/redirects.js
Outdated
const passed = pageRedirects.length <= REDIRECT_TRESHOLD; | ||
if (!passed) { | ||
debugString = `Your page has ${pageRedirects.length} redirects.` + | ||
' Redirects introduce additional delays before the page can be loaded.'; |
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 guess we dont need this second sentence since its already in helptext
65e45ac
to
3e0f9d9
Compare
lighthouse-core/audits/redirects.js
Outdated
} | ||
|
||
static isRedirect(requestKey) { | ||
return requestKey.includes('redirected'); |
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.
it looks like we depend here on 'redirected' being used in the name of request ID. IMO it'd be better to use something more solid likeWebInspector.NetworkRequest.redirects
or request status code.
Since I'm dealing with a similar thing here: #3311 . How about creating a MainResource
computed artifact that exposes WebInspector.NetworkRequest.redirects
that we both could use?
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.
@kdzwinel Should we name it more like getMainResourceChain?
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.
@kdzwinel I think we have a slightly different use case :) I need to get the redirects as well so I cannot use the computed artifact
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.
@wardpeet maybe I'm missing something, but when you request a MainResource artifact you also get all NetworkRequest (that contain all timing info) representing all redirects that led to that resource. Check out this:
Isn't that all you need to compute the time wasted in redirects?
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.
ah sorry I missed that! Great!
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.
Awesome 🙌 I already got one 👍 on my PR, so I hope to merge soon.
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.
MainResource computed artifact sgtm, we'll prob want to move away from more "chain-like" computed artifacts though going forward and try to make use of the page-dependency-graph
instead
const cacheBuster = Number(new Date()); | ||
module.exports = [ | ||
{ | ||
initialUrl: `http://localhost:10200/online-only.html?cb=${cacheBuster}&delay=500&redirect=%2Foffline-only.html%3Fcb=${cacheBuster}%26delay=500%26redirect%3D%2Fredirects-final.html`, |
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.
ha this is quite the URL :)
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.
sweet I like the look of this one!
url: 'http://localhost:10200/redirects-final.html', | ||
audits: { | ||
'redirects': { | ||
score: 2, |
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.
let's assert on details length instead
url: 'http://localhost:10200/redirects-final.html', | ||
audits: { | ||
'redirects': { | ||
score: 1, |
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.
same
lighthouse-core/audits/redirects.js
Outdated
|
||
return { | ||
debugString, | ||
score: pageRedirects.length, |
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.
score actually has meaning for the opportunities and is used to color the bar based on how bad the offending opportunity was, they can still get this information from the details length
8da4782
to
f9db70a
Compare
@patrickhulce Updated the PR :) what do you think? |
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.
thanks for the quick turnaround @wardpeet! :)
chrome-launcher/ask.js
Outdated
@@ -0,0 +1,32 @@ | |||
/** |
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.
revert these? :)
lighthouse-core/audits/redirects.js
Outdated
|
||
return { | ||
debugString, | ||
score: passed, |
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.
oops, maybe I wasn't very clear last time my bad!
score is automatically determined for the performance opportunities, let's reuse that
score: UnusedBytes.scoreForWastedMs(wastedMs), |
lighthouse/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js
Lines 21 to 30 in 4b8d8a1
/** | |
* @param {number} wastedMs | |
* @return {number} | |
*/ | |
static scoreForWastedMs(wastedMs) { | |
if (wastedMs === 0) return 100; | |
else if (wastedMs < WASTED_MS_FOR_AVERAGE) return 90; | |
else if (wastedMs < WASTED_MS_FOR_POOR) return 65; | |
else return 0; | |
} |
url: 'http://localhost:10200/redirects-final.html', | ||
audits: { | ||
'redirects': { | ||
score: false, |
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.
once the numeric score is brought back, let's assert that this score is in the lowest bracket
46255c4
to
569e407
Compare
lighthouse-core/audits/redirects.js
Outdated
let debugString = null; | ||
|
||
const pageRedirects = redirectRequests.map(request => { | ||
const wastedMs = (request.endTime - request.startTime) * 1000; |
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 don't think this is what we want here, from the screenshot of gmail it looks like request.endTime
is the end time of the final mainResource, I think we want something more like requests[1].startTime - requests[0].startTime
?
lighthouse-core/audits/redirects.js
Outdated
}; | ||
}); | ||
|
||
const passed = pageRedirects.length <= REDIRECT_THRESHOLD; |
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.
let's go ahead and nuke the passed and debugString now that passed isn't needed, hopefully its clear from the table how many redirects they had :)
lighthouse-core/audits/redirects.js
Outdated
|
||
const headings = [ | ||
{key: 'url', itemType: 'text', text: 'URL'}, | ||
{key: 'wastedMs', itemType: 'text', text: 'Wasted ms'}, |
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.
nit: let's set the text to "Time for Redirect"
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.
looks great! I'm ready to approve % open question about how to handle totalWastedMs :)
lighthouse-core/audits/redirects.js
Outdated
name: 'redirects', | ||
description: 'Avoids page redirects.', | ||
failureDescription: 'Has page redirects.', | ||
helpText: ' Redirects introduce additional delays before the page can be loaded. [Learn more](https://developers.google.com/speed/docs/insights/AvoidRedirects).', |
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.
nit: remove space at beginning
lighthouse-core/audits/redirects.js
Outdated
const request = redirectRequests[i - 1]; | ||
const nextRequest = redirectRequests[i]; | ||
const wastedMs = (nextRequest.startTime - request.startTime) * 1000; | ||
totalWastedMs += wastedMs; |
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 liked what you had about allowing a single redirect before like PSI, wdyt about excluding the first redirect cost from totalWastedMs
but still reporting how long it took?
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 as we should report all redirects as a cost is a cost +1
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.
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.
nicely done! 🎉 💯
to you @paulirish if you still have lingering requested changes
lighthouse-core/audits/redirects.js
Outdated
category: 'Performance', | ||
name: 'redirects', | ||
description: 'Avoids page redirects.', | ||
failureDescription: 'Has page redirects.', |
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.
nit: I'd prefer adding an 'excessive' or some qualifier here so it's clear you can have one
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'm not that good with words in english so what about
Has more than one page redirects
?
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.
heh sure Has more than one page redirect
👍
audits: { | ||
'redirects': { | ||
score: 100, | ||
rawValue: '>=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.
nit: we can assert 0 exactly here, right?
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.
sorry, drive-by nits on just the smoke testing part :)
@@ -51,7 +51,8 @@ function requestHandler(request, response) { | |||
} | |||
|
|||
function sendResponse(statusCode, data) { | |||
let headers; | |||
let headers = {}; |
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 change not needed anymore?
@@ -51,7 +51,8 @@ function requestHandler(request, response) { | |||
} | |||
|
|||
function sendResponse(statusCode, data) { | |||
let headers; | |||
let headers = {}; | |||
let delay = 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.
maybe move delay
declaration to below the filePath.endsWith
set of conditionals since it's not used until inside if (queryString) {}
?
|
||
// redirect url to new url if present | ||
if (typeof queryString.redirect !== 'undefined') { | ||
if (delay > 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.
since delay
defaults to 0
, for code readability this could just drop the delay
check and do return setTimeout(sendRedirect, delay, queryString.redirect);
for both
} | ||
|
||
response.writeHead(statusCode, headers); | ||
|
||
// Delay the response by the specified ms defaulting to 2000ms for non-numeric values | ||
if (queryString && typeof queryString.delay !== 'undefined') { | ||
response.write(''); |
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 response.write('');
important here? I have no idea :) but it was here since the first version
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 don't think so as response.write('');
as response.end() is important to close the request.
maybe @paulirish still knows why he added it.
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 barely remember it being important. is it fine if we keep it as is?
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.
keep it as is = add the response.write('')?
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.
okay scratch what i said. response.write('')
is a noop.
.write('.')
does change things quite considerably, but in a bad direction. (the latency moves from TTFB to content download).
your change here is great. let's keep it. :)
|
||
module.exports = [ | ||
{ | ||
initialUrl: `http://localhost:10200/online-only.html?cb=${cacheBuster}&delay=500&redirect=%2Foffline-only.html%3Fcb=${cacheBuster}%26delay=500%26redirect%3D%2Fredirects-final.html`, |
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.
smokehouse should be opening a new instance of chrome for each expectations entry, so are the cache busters necessary?
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.
Probably not 👍
@paulirish I can add it to the table :) give me a sec 😸 |
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 have some proposed superficial changes. I'd like to merge this PR as it is and i'll followup with a PR for ward to review.
} | ||
|
||
response.writeHead(statusCode, headers); | ||
|
||
// Delay the response by the specified ms defaulting to 2000ms for non-numeric values | ||
if (queryString && typeof queryString.delay !== 'undefined') { | ||
response.write(''); |
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.
okay scratch what i said. response.write('')
is a noop.
.write('.')
does change things quite considerably, but in a bad direction. (the latency moves from TTFB to content download).
your change here is great. let's keep it. :)
Fixes #3210
fixes #605
Looking at psi it allows 1 redirect (not sure if we should only consider subdomains as allowed redirects).
https://example.com -> https://m.example.com
Report:
Perhaps we should use start & end time to calculate wasted ms?