-
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
core(installable-manifest): use devtools InstallabilityErrors #11745
Conversation
Since this is in draft state, can you specify which parts you'd like feedback on, and what things you already know needs more work? |
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 comments on some things that immediately jump out at me :)
lighthouse-cli/test/smokehouse/test-definitions/offline-local/offline-expectations.js
Show resolved
Hide resolved
lighthouse-cli/test/smokehouse/test-definitions/pwa/pwa-expectations.js
Outdated
Show resolved
Hide resolved
score: 0, | ||
details: {items: [jakeExpectations]}, | ||
explanation: /^Failures: .*short_name/, | ||
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.
does this mean "Manifest does not contain a 'name' or 'short_name' field
" only requires one of name
and short_name
, so is passing now?
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 coupla weeks ago i spent a while looking at https://source.chromium.org/chromium/chromium/src/+/master:chrome/browser/installable/installable_manager.cc;l=651-655;drc=05dff50d9ce188e04ef796f0f1c28fbef09ace59 and my brain still doesn't make sense of it.
it seeems to say:
if (nameIsMissingOrEmpty && shortNameIsMissingOrEmpty) {
errors.push('MANIFEST_MISSING_NAME_OR_SHORT_NAME')
}
and how does && in the logic match up with a var named OR.
in fact it seemed so wrong that i figured it might be a bug. so adriana and I made a CL: https://chromium-review.googlesource.com/c/chromium/src/+/2530481
but the tests are all failureful.
so tbh i have no idea.
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.
The fact that this makes sense to me now has me worried about my understanding... 😆
I think the MANIFEST_MISSING_NAME_OR_SHORT_NAME
is just ambiguously named because when interpreting it as "manifest missing something - name or short name needed", which is consistent with my understanding that you need something to use as the name, is all works out. You definitely don't need both name and short_name.
It's not the case for installability that you need a short_name
, right? I believe Lighthouse just did so because we thought it was a good idea for most apps, but now that we've switched I would expect the &&
to fail the tests and our result to change.
/** Error message explaining that a URL in the manifest contains a username, password, or port. */ | ||
'url-not-supported-for-webapk': `A URL in the manifest contains a username, password, or port`, | ||
/** Error message explaining that the page is loaded in an incognito window. */ | ||
'in-incognito': `Page is loaded in an incognito window`, |
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 recommend users with busy extensions or important storage use incognito to test. Does this mean PWA testing won't work 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.
yes this is accurate.
:/
we could choose to special-case this one...
we probably are special casing this "warn" error that's landing soon: https://chromium-review.googlesource.com/c/chromium/src/+/2567057
for (const err of installabilityErrors) { | ||
// @ts-expect-error errorIds from protocol should match up against the strings dict | ||
const matchingString = UIStrings[err.errorId]; | ||
// We only expect a `minimum-icon-size-in-pixels` errorArg[0] for two errorIds, currently. |
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 will throw if this statement becomes not true at some point in the future (new error type or whatever), so better to check this as well :)
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.
yeaaaaah true
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 brand new error id that our code doesn't handle will fall into L157.
an existing error id that our code handles could have an arg added to it, and we'd fall into L164 but the error message string wouldn't handle the values. i18n will throw. https://github.com/GoogleChrome/lighthouse/blob/master/lighthouse-core/lib/i18n/i18n.js#L267
we could avoid errors if _str
somehow provided how many arguments a formatted string has, and check if that is equal to errorArguments.length
, and if not we fallback to a generic string (${errorId} ${errorArguments.join(', ')}
). But _str
doesn't provide # of args. We could do a string search for value#
for each index in errorArguments
as a substitute ...
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.
@connorjclark What do you think about having something like this?
const errorArgumentsRegex = /{([^}]+)}/g;
const UIStringArguments = matchingString.match(errorArgumentsRegex) || [];
if (matchingString && value0) {
errorMessages.push(str_(matchingString, {value0}));
} else if (matchingString && err.errorArguments.length !== UIStringArguments.length) {
// Matching string, but have the incorrect number of arguments for the message.
errorMessages.push(str_(UIStrings.notEnoughArguments, {errorId: err.errorId}))
} else if (matchingString) {
errorMessages.push(str_(matchingString));
} else {
errorMessages.push(str_(UIStrings.noErrorId));
}
Where the notEnoughArguments
string would be the generic string you mentioned. Although it doesn't include the args in the string. Somewhat similar to how Devtools does 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.
looks good, that's a tricky regex. couple things
- the first case is still susceptible to an error (if we get an unexpected number of args for an error we already handle).
- the check for
matchingString && err.errorArguments.length !== UIStringArguments.length
should go first, that'd solve thing 1) - let's add a second arg to
UIStrings.notEnoughArguments
, so the string can attempt to be useful (at the risk of printing randomish data). I wonder if we even should use i18n here. @exterkamp the string would be${errorId} ${errorArguments.join(', ')
... any point to send that to translators?
I suspect the "1 arg case" is fine forever, even if a "2 arg case" comes up still just duplicating a little code instead of making a generic "n arg case" is much better to read.
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.
shane says let's avoid UI strings/i18n for the "notEnoughArguments" case
/** Error message explaining that a URL in the manifest contains a username, password, or port. */ | ||
'url-not-supported-for-webapk': `A URL in the manifest contains a username, password, or port`, | ||
/** Error message explaining that the page is loaded in an incognito window. */ | ||
'in-incognito': `Page is loaded in an incognito window`, |
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.
yes this is accurate.
:/
we could choose to special-case this one...
we probably are special casing this "warn" error that's landing soon: https://chromium-review.googlesource.com/c/chromium/src/+/2567057
for (const err of installabilityErrors) { | ||
// @ts-expect-error errorIds from protocol should match up against the strings dict | ||
const matchingString = UIStrings[err.errorId]; | ||
// We only expect a `minimum-icon-size-in-pixels` errorArg[0] for two errorIds, currently. |
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.
yeaaaaah true
lighthouse-cli/test/smokehouse/test-definitions/offline-local/offline-expectations.js
Show resolved
Hide resolved
lighthouse-cli/test/smokehouse/test-definitions/pwa/pwa-expectations.js
Outdated
Show resolved
Hide resolved
score: 0, | ||
details: {items: [jakeExpectations]}, | ||
explanation: /^Failures: .*short_name/, | ||
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.
😆
a coupla weeks ago i spent a while looking at https://source.chromium.org/chromium/chromium/src/+/master:chrome/browser/installable/installable_manager.cc;l=651-655;drc=05dff50d9ce188e04ef796f0f1c28fbef09ace59 and my brain still doesn't make sense of it.
it seeems to say:
if (nameIsMissingOrEmpty && shortNameIsMissingOrEmpty) {
errors.push('MANIFEST_MISSING_NAME_OR_SHORT_NAME')
}
and how does && in the logic match up with a var named OR.
in fact it seemed so wrong that i figured it might be a bug. so adriana and I made a CL: https://chromium-review.googlesource.com/c/chromium/src/+/2530481
but the tests are all failureful.
so tbh i have no idea.
@adrianaixba is the status of this PR correct, awaiting a re-review from @paulirish ? |
One more pass would be nice :) |
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.
lg!
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, last things :)
let matchingString; | ||
try { | ||
// @ts-expect-error errorIds from protocol should match up against the strings dict | ||
matchingString = UIStrings[err.errorId]; |
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 line is a potential typescript error, but it won't ever throw at runtime (it'll just be
undefined
). Can just do anif (matchingString === undefined) {
check (though the compiler may complain about that too)
this doesn't seem like it got changed?
Created new issue for this #11826 :) |
artifacts.WebAppManifest = null; | ||
const context = generateMockAuditContext(); | ||
|
||
return InstallableManifestAudit.audit(artifacts, context).then(result => { | ||
assert.strictEqual(result.score, 0); | ||
assert.ok(result.explanation.includes('No manifest was fetched'), result.explanation); | ||
const items = result.details.items; | ||
assert.ok(items[0].reason.formattedDefault.includes('no manifest')); |
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.
assert.ok(items[0].reason.formattedDefault.includes('no manifest')); | |
expect(items[0].reason).toBeDisplayString(/no manifest/); |
this is the "proper" way to test the localized version of the string (or i18n.getFormatted(items[0].reason, 'en-US').includes('no manifest')
) Mostly the same in practice and in theory, but
- doesn't reach into the internal representation (allowing for easier i18n refactors in the future) and
- is testing the final string coming out of the i18n pipeline, so it's closer to what will be in the actual report (
formattedDefault
is the fallback in case that pipeline fails)
mainly I expect people will be copy/pasting from these tests in the future so it's good to do it the slightly better way.
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 very clean, i like it 👀
@@ -35,12 +35,14 @@ describe('PWA: webapp install banner audit', () => { | |||
describe('basics', () => { |
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.
really a lot of these tests could be dropped now since most of them are just testing are errorIds mapped to strings in the items, but there's not any harm in leaving them in place
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, mainly still just test coverage
description: 'Browsers can proactively prompt users to add your app to their homescreen, ' + | ||
'which can lead to higher engagement. ' + | ||
'[Learn more](https://web.dev/installable-manifest/).', | ||
'description': `Service worker is the technology that enables your app to use many Progressive Web App features, such as offline, add to homescreen, and push notifications. With proper Service Worker and manifest implementations, browsers can proactively prompt users to add your app to their homescreen, which can lead to higher engagement. [Learn more](https://web.dev/installable-manifest/).`, |
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.
'description': `Service worker is the technology that enables your app to use many Progressive Web App features, such as offline, add to homescreen, and push notifications. With proper Service Worker and manifest implementations, browsers can proactively prompt users to add your app to their homescreen, which can lead to higher engagement. [Learn more](https://web.dev/installable-manifest/).`, | |
'description': `Service worker is the technology that enables your app to use many Progressive Web App features, such as offline, add to homescreen, and push notifications. With proper service worker and manifest implementations, browsers can proactively prompt users to add your app to their homescreen, which can lead to higher engagement. [Learn more](https://web.dev/installable-manifest/).`, |
we should update the web.dev article soon to include the content from the other service-worker audits
'url-not-supported-for-webapk': `A URL in the manifest contains a username, password, or port`, | ||
/** Error message explaining that the page is loaded in an incognito window. */ | ||
'in-incognito': `Page is loaded in an incognito window`, | ||
// TODO: perhaps edit this message to make it more actionable for LH users |
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 will get lost in here, so should this be filed as an issue or is it covered enough now that it could be removed?
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.
done! #11830 Yeah these are messages that we might want to communicate with the installability/devtools folks about, to get a clearer definition of for LH
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!
Use devtools InstallabilityErrors, to reduce the variability between devtools and the lighthouse report.
fixes #10515 #10237
Follow ups:
Future action:
Possible future additions (not possible right now, I believe):