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: special case AppManifest as baseArtifact #6957

Merged
merged 8 commits into from
Jan 17, 2019

Conversation

patrickhulce
Copy link
Collaborator

Summary
See #6882 (comment)

Cleans up our start URL gatherer to not fetch and parse the manifest again.

Related Issues/PRs
@brendankenny's preferred implementation of #6882
closes #6881

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.

I like this :)

@@ -29,6 +29,8 @@ declare global {
NetworkUserAgent: string;
/** The benchmark index that indicates rough device class. */
BenchmarkIndex: number;
/** The application manifest if one was fetched. */
AppManifest: Artifacts.Manifest | null;
Copy link
Member

Choose a reason for hiding this comment

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

to me this makes me question if it's actually related to an appcache manifest :) If we want to be more precise, how do you feel about WebAppManifest?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

someone should tell driver and the devtools protocol then 😛

that wfm 👍

* @param {LH.Gatherer.PassContext} passContext
* @return {Promise<LH.Artifacts['Manifest']>}
*/
static async getAppManifest(passContext) {
Copy link
Member

Choose a reason for hiding this comment

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

should we merge most of this into driver.getAppManifest()? Not the baseArtifacts check, of course, and probably not the parse step(?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah good idea 👍


const manifestPromise = passContext.driver.getAppManifest();
/** @type {Promise<void>} */
const timeoutPromise = new Promise(resolve => setTimeout(resolve, 3000));
Copy link
Member

Choose a reason for hiding this comment

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

I think we can remove this with the default timeouts now

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I like the idea of keeping it short though, I'll set it to 3s 👍

* Uses the debugger protocol to fetch the manifest from within the context of
* the target page, reusing any credentials, emulation, etc, already established
* there. The artifact produced is the fetched string, if any, passed through
* The artifact produced is the fetched manifest string, if any, passed through
* the manifest parser.
*/
class Manifest extends Gatherer {
Copy link
Member

Choose a reason for hiding this comment

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

any reason to keep this? TODO for followup to split up the changes nicely? Or is it worth keeping for some reason?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

meh, I'll just do it now

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

boy am I regretting that 😆

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah I'm gonna make this a TODO

@@ -20,6 +20,7 @@ declare global {
options?: object;
/** Push to this array to add top-level warnings to the LHR. */
LighthouseRunWarnings: Array<string>;
baseArtifacts: BaseArtifacts;
Copy link
Member

Choose a reason for hiding this comment

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

we might want to play around with Readonly for this, but that can be something for another day :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

👍

@@ -61,7 +54,7 @@ class StartUrl extends Gatherer {
* @param {string} startUrl
* @return {Promise<{statusCode: number, explanation: string}>}
*/
_attemptManifestFetch(driver, startUrl) {
_attemptStartURLFetch(driver, startUrl) {
Copy link
Member

Choose a reason for hiding this comment

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

ha, whoops

@brendankenny
Copy link
Member

looks like some fragile mocks broke :S

@patrickhulce
Copy link
Collaborator Author

@brendankenny this is ready for a second look now btw

@@ -29,6 +29,8 @@ declare global {
NetworkUserAgent: string;
/** The benchmark index that indicates rough device class. */
BenchmarkIndex: number;
/** Parsed version of the page's Web App Manifest, or null if none found.. */
Copy link
Member

Choose a reason for hiding this comment

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

no double .

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done


describe('.getAppManifest', () => {
it('should return null when no manifest', async () => {
const sendCommand = jest.spyOn(connection, 'sendCommand');
Copy link
Member

Choose a reason for hiding this comment

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

spyOn is a little hard to read to me because I expect it to be spied upon at that point. How do you feel about just doing the plain connection.sendCommand = jest.fn().mockResolvedValueOnce({data: undefined, url: '/manifest'});?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

sure that's fine

* will have the reason. See manifest-parser.js for more information.
*
* @param {LH.Gatherer.PassContext} passContext
* @return {Promise<LH.Artifacts['Manifest']>}
Copy link
Member

Choose a reason for hiding this comment

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

since we're going to eventually ditch LH.Artifacts['Manifest'], switch to LH.Artifacts['WebAppManifest'] (or LH.Artifacts.Manifest|null)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

@@ -467,6 +493,7 @@ class GatherRunner {
}
await GatherRunner.beforePass(passContext, gathererResults);
await GatherRunner.pass(passContext, gathererResults);
baseArtifacts.WebAppManifest = await GatherRunner.getWebAppManifest(passContext);
Copy link
Member

Choose a reason for hiding this comment

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

we have two other checks against isFirstPass already in this method, can we move the check for this line out here too and avoid putting it on passContext (and it'll be more obvious we aren't overwriting it on every turn of the loop)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

sure, done

}

/**
* Read the parsed manifest and return failure reasons or the startUrl
* @param {?{value?: {start_url: {value: string, warning?: string}}, warning?: string}} manifest
* @param {LH.Artifacts['Manifest']} manifest
Copy link
Member

Choose a reason for hiding this comment

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

LH.Artifacts['WebAppManifest']

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

assert.equal(artifact.explanation, 'Timed out waiting for fetched start_url.');
});
const resultPromise = startUrlGatherer.afterPass(passContext);
jest.advanceTimersByTime(5000);
Copy link
Member

Choose a reason for hiding this comment

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

wellllllll this is nice

driver: wrapSendCommand(mockDriver, 'https://ifixit-pwa.appspot.com/', 200, true),
};
const optionsWithQueryString = {
const passContextWithFragment = {
Copy link
Member

Choose a reason for hiding this comment

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

:)

});

afterEach(() => {
jest.advanceTimersByTime(5000);
Copy link
Member

Choose a reason for hiding this comment

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

why is this needed after each test is done?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

the other tests not touched by this don't properly cleanup their timer, I'll add a todo to fix this another time?

Copy link
Member

Choose a reason for hiding this comment

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

the other tests not touched by this don't properly cleanup their timer, I'll add a todo to fix this another time?

yeah, sounds good. I think advanceTimersByTime is going to be a game changer for a lot of our older tests and it would be nice to update anyways

*/
static async getWebAppManifest(passContext) {
// If we already have a manifest, return it.
if (passContext.baseArtifacts.WebAppManifest) return passContext.baseArtifacts.WebAppManifest;
Copy link
Member

Choose a reason for hiding this comment

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

not needed with the isFirstPass check below?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fair, removed

Copy link
Member

@paulirish paulirish left a comment

Choose a reason for hiding this comment

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

a lot cleaner to my eyes. excited for it.

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.

yeah! love it

});

afterEach(() => {
jest.advanceTimersByTime(5000);
Copy link
Member

Choose a reason for hiding this comment

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

the other tests not touched by this don't properly cleanup their timer, I'll add a todo to fix this another time?

yeah, sounds good. I think advanceTimersByTime is going to be a game changer for a lot of our older tests and it would be nice to update anyways

@brendankenny brendankenny merged commit fb6c376 into master Jan 17, 2019
@brendankenny brendankenny deleted the manifest_base_artifacts branch January 17, 2019 20:53
@tmb-github
Copy link

Whatever's been done with the PWA tester in Lighthouse seems to have (or may have) broken it with regards to testing Performance in the other parts of the audit, as well as blocked the PWA audit score as well.

In Chrome 71 (still current as I write this), this site gets 100% on performance and PWA; in the Chrome 74.0.3686.0 available today, this site gets 0% performance and 0% PWA despite 100 in all PWA categories except 2 of them:

https://ives-fourth-symphony.com

This may or may not be related to the manifest fetching rewrite, but the coincidence of the two prompted me to report this here.

@patrickhulce
Copy link
Collaborator Author

Thanks @tmb-github! The version of Lighthouse in Chrome doesn't have this commit yet, and locally this hash passes on that site, so looks like they're unrelated.
image

Is it possible you're bumping into #6968?

@tmb-github
Copy link

Please try the same audit again on the new stable Chrome 72.0.3626.81 (Official Build) (64-bit), released today. Nothing can be fetched for the PWA audit nor the Performance audit.

@tmb-github
Copy link

untitled

@patrickhulce
Copy link
Collaborator Author

Thanks @tmb-github it's due to some infrastructure serving outdated version of audits panel that's incompatible with newer Chrome. The team is on it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Lighthouse unable to fetch manifest.json, favicons in Canary 73.0.3654.0
4 participants