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

feat: add CSS usage tracking #1421

Merged
merged 12 commits into from
Jan 11, 2017
Merged

feat: add CSS usage tracking #1421

merged 12 commits into from
Jan 11, 2017

Conversation

patrickhulce
Copy link
Collaborator

R: all

Adds basic CSS rule usage tracking gatherer and audit. Moves toward parity with Audits 1.0 as per #909.

Has a couple of issues that need to be figured out.

  • Presentation could use some work
    It's using the existing URLLIST formatter that the no-old-flexbox audit uses but it'd be nice to group these by stylesheet ID, emphasize selector over URL, etc.
  • False positives and information overload limit its usefulness
    The Verge gives 4386 unused rules and dumps in a gigantic list, also unable to detect soon-to-be-used rules that still make sense to load with the page and failing for 1 unused rule (which could just be media-query or something) seems harsh and at risk of being ignored as flaky.
  • URL Information is lacking
    I was unable to get the urls of inline styles that were added via script. I wouldn't be concerned but since this is a core behavior of the webpack style-loader it seems reasonable that we'll run into it fairly often, and it'd be nice to have the url of the script that added them at least (not sure how this would play into source maps or if we can leverage)

image

Adds basic CSS rule usage tracking gatherer and audit. Moves toward parity with Audits 1.0 as per #909.
"name": "Using bytes efficiently",
"audits": {
"unused-css-rules": {
"expectedValue": false
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This doesn't make sense to me, shouldn't the expectedValue be true?

Copy link
Member

Choose a reason for hiding this comment

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

didn't you write this code? :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

haha :P I'm commenting on why I had to set it to false for it to become an error when the value is true when successful

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

followup: all the expectedValue entries here seem to be useless for non-scored sections, the report just wants true for success and false for failure, I can toggle this all day with no change in the report, but the rawValue === didPass

@@ -115,9 +115,7 @@ class Styles extends Gatherer {
Promise.all(contentPromises).then(styleHeaders => {
driver.off('CSS.styleSheetAdded', this._onStyleSheetAdded);
driver.off('CSS.styleSheetRemoved', this._onStyleSheetRemoved);
return driver.sendCommand('CSS.disable')
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not sure if this is the right time to pull out domain information when multiple gatherers use the same domain, but for now just manually removing here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

or maybe just a new cleanup pass?

Copy link
Contributor

Choose a reason for hiding this comment

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

We were bound to run into this eventually. Need to come up with a general solution.

At the very least for this PR, can you comment this out and make a note of why they're commented?

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

@ebidel
Copy link
Contributor

ebidel commented Jan 6, 2017

The Verge gives 4386 unused rules and dumps in a gigantic list, also unable to detect soon-to-be-used rules that still make sense to load with the page and failing for 1 unused rule (which could just be media-query or something) seems harsh and at risk of being ignored as flaky.

@patrickhulce if you run the audits panel on the verge.com, does it come back with 4386 unused styles? IOW, does it reach parity or are you trying to do even better than the audits panel?:)

@patrickhulce
Copy link
Collaborator Author

@ebidel we do a little better than current audits. It returns 2393 unused :) They might restrict to same-origin currently though since the difference appears to be from https://s.ytimg.com/yts/cssbin/www-embed-player-sprite-mode-vfl0Ytuip.css

@patrickhulce
Copy link
Collaborator Author

@ebidel @brendankenny FYI tentatively removing WIP.

I've attempted to address the 3 concerns above by

  • Just reporting on each stylesheetId and its url
  • Cleanly displaying a preview of the stylesheet content for inline sheets
  • Only failing the audit if 10+% of rules are unused (arbitrary value and thoughts on a data-driven or perhaps byte-based value is welcome in future PR)

@patrickhulce patrickhulce changed the title [WIP] feat: add CSS usage tracking feat: add CSS usage tracking Jan 9, 2017
@@ -129,13 +133,17 @@ class Styles extends Gatherer {
afterPass(options) {
return this.endStylesCollect(options.driver)
.then(stylesheets => {
// Want unique stylesheets. Remove those with the same text content.
// Generally want unique stylesheets. Mark those with the same text content.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not sure where we actually needed to do this? It looks like the only other usage of Styles is the no flexbox which I would advocate warning even on multiple shadow DOM uses, etc

Copy link
Contributor

Choose a reason for hiding this comment

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

Shadow DOM is unique here AFAICT. When testing a page with 100 components that all share the same stylesheet, we'll get 100 violations. That ends up being pretty noisy and something I noticed immediately on testing some sites built in Polymer. We should definitely keep.

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 guess my question is, what audits are noisy? If you use flexbox in 100 style elements in all 100 of your shadow doms, shouldn't you have 100 failures? I couldn't find where else we use it.

Copy link
Contributor

Choose a reason for hiding this comment

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

IMO no, because the author is writing the <style> once and creating instances of the custom element. IOW, I don't think it's helpful to know you're failing 100 times. You really just want to know the source of failure, then fix 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.

fair enough, added the appropriate filter to the usage function

@@ -0,0 +1,113 @@
/**
* @license
* Copyright 2016 Google Inc. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

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

2017

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

name: 'unused-css-rules',
description: 'Site does not have more than 10% unused CSS',
helpText: 'Remove unused rules from stylesheets to reduce unnecessary ' +
'bytes consumed by network activity.',
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add a learn more link? Maybe [Learn more](https://developers.google.com/speed/docs/insights/OptimizeCSSDelivery) for now.

cc @kaycebasques for the FYI.

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

};
}

static indexStylesheetsById(styles) {
Copy link
Contributor

Choose a reason for hiding this comment

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

doc styles

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

}
});

return {unused: unused, total: rules.length};
Copy link
Contributor

Choose a reason for hiding this comment

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

because it's fun to shorten objects with same key/val names: {unused, total: rules.length};

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 (by getting rid of entirely)

@@ -129,13 +133,17 @@ class Styles extends Gatherer {
afterPass(options) {
return this.endStylesCollect(options.driver)
.then(stylesheets => {
// Want unique stylesheets. Remove those with the same text content.
// Generally want unique stylesheets. Mark those with the same text content.
Copy link
Contributor

Choose a reason for hiding this comment

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

Shadow DOM is unique here AFAICT. When testing a page with 100 components that all share the same stylesheet, we'll get 100 violations. That ends up being pretty noisy and something I noticed immediately on testing some sites built in Polymer. We should definitely keep.

@@ -35,6 +35,12 @@ function getCSSPropsInStyleSheet(parseTree) {
const results = [];

parseTree.traverseByType('declaration', function(node, index, parent) {
if (parent.type === 'arguments') {
// We don't want to return data URI delcarations of the form
Copy link
Contributor

Choose a reason for hiding this comment

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

declarations

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

@@ -115,9 +115,7 @@ class Styles extends Gatherer {
Promise.all(contentPromises).then(styleHeaders => {
driver.off('CSS.styleSheetAdded', this._onStyleSheetAdded);
driver.off('CSS.styleSheetRemoved', this._onStyleSheetRemoved);
return driver.sendCommand('CSS.disable')
Copy link
Contributor

Choose a reason for hiding this comment

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

We were bound to run into this eventually. Need to come up with a general solution.

At the very least for this PR, can you comment this out and make a note of why they're commented?

@@ -0,0 +1,187 @@
/**
* Copyright 2016 Google Inc. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

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

2017

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

const Audit = require('../../audits/unused-css-rules.js');
const assert = require('assert');

/* global describe, it, beforeEach */
Copy link
Contributor

Choose a reason for hiding this comment

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

/* eslint-env mocha */

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


function generate(content, length) {
const arr = [];
for (let i=0; i<length; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

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

spacing: for (let i = 0; i < length; i++) {

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

@ebidel
Copy link
Contributor

ebidel commented Jan 9, 2017

Think the CI is failing b/c of some node4/5 es6 features not being supported.

@ebidel
Copy link
Contributor

ebidel commented Jan 10, 2017

LGTM from me.

@patrickhulce
Copy link
Collaborator Author

@brendankenny any more thoughts?

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.

looking good.

JSDoc comments are optional, as I was mostly using that as a way to walk through the PR and we don't really use them right now :)

const styles = artifacts.Styles;
const usage = artifacts.CSSUsage;

if (typeof styles === 'undefined' || typeof usage === 'undefined') {
Copy link
Member

Choose a reason for hiding this comment

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

this first block is no longer necessary because runner now checks that all required artifacts are defined before running an audit

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

woohoo! done

}

/**
* @param {!Array.<Object>} styles The output of the Styles gatherer.
Copy link
Member

Choose a reason for hiding this comment

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

could do @param {!Array<{header: {styleSheetId: string}}>}

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


/**
* @param {!Array.<Object>} styles The output of the Styles gatherer.
* @return {!Object} A map of styleSheetId to stylesheet information.
Copy link
Member

Choose a reason for hiding this comment

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

@return {{used: !Array, unused: !Array}}?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's a subset of the type of each value under the keys of the object. It's basically a map of stylesheet id -> stylesheet + that type.

Copy link
Member

Choose a reason for hiding this comment

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

right, doing records like that is structural typing, so you really only need to declare what you're using. If we had a compiler looking at this, though, you'd have to cast between calling these functions, so I guess if you really wanted to do this you'd need to use the union of my suggestions for each of these, something like {{used: !Array, unused: !Array, content: string, header: {sourceURL: string, styleSheetId: string}}}, but, again, may not be worth it if we're not doing typedefs :)

Copy link
Member

Choose a reason for hiding this comment

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

sorry, yeah, didn't understand what you meant here. I had this wrong. As you point out, it would be @return Object<{used: !Array, unused: !Array}>, if you wanted to change it :)


/**
* Counts the number of unused rules and adds count information to sheets.
* @param {!Array.<RuleUsage>} rules The output of the CSSUsage gatherer.
Copy link
Member

Choose a reason for hiding this comment

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

is RuleUsage defined anywhere? Can also do {!Array<{styleSheetId: string, used: boolean}>}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's in the devtools protocol. I saw other usage of devtools protocol classes, but maybe we just had our own corresponding classes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

will change

* @return {!Object} A map of styleSheetId to stylesheet information.
*/
static indexStylesheetsById(styles) {
return styles.reduce((indexed, stylesheet) => {
Copy link
Member

Choose a reason for hiding this comment

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

looks like devtools does styleSheet (e.g. styleSheetId) but you mostly do stylesheet. We should settle on one, preferably

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'll change to theirs

.then(_ => options.driver.sendCommand('CSS.startRuleUsageTracking'));
}

gatherRuleUsage(driver) {
Copy link
Member

Choose a reason for hiding this comment

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

total nit: any reason not to inline this into afterPass as the above is inlined into beforePass?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

just that I thought it was going to be more complicated when I first started writing the file :) done

return Array.from(map.values());
return stylesheets.map(stylesheet => {
const idInMap = map.get(stylesheet.content).header.styleSheetId;
stylesheet.isDuplicate = idInMap !== stylesheet.header.styleSheetId;
Copy link
Member

Choose a reason for hiding this comment

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

This appears to be unused right now?

@ebidel you ok with this no longer removing duplicates per conversation above?

Copy link
Contributor

Choose a reason for hiding this comment

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

Choose a reason for hiding this comment

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

อย่างไรดีวิธีการที่รวดเร็ว

*/
'use strict';

const Audit = require('../../audits/unused-css-rules.js');
Copy link
Member

Choose a reason for hiding this comment

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

will you call this unusedCssAudit or something? Much easier to follow when reading in the future :)

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('#audit', () => {
it('fails when no input present', () => {
Copy link
Member

Choose a reason for hiding this comment

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

can remove this as runner will always call with artifacts 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.

done, yeah I thought we had gotten rid of these but the test file I copied still had it in there so I thought I was misremembering haha

});
});

describe('#audit', () => {
Copy link
Member

Choose a reason for hiding this comment

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

just make these top level?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

is there a reason behind not nesting?

Copy link
Member

Choose a reason for hiding this comment

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

no, it doesn't really matter, just that the whole test is the #audit test :)

Choose a reason for hiding this comment

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

ทำต่อไปครับ

static indexStylesheetsById(styles) {
return styles.reduce((indexed, stylesheet) => {
indexed[stylesheet.header.styleSheetId] = Object.assign({
used: [],
Copy link
Member

Choose a reason for hiding this comment

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

is it worth having these as arrays or should you just do counts?

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 don't think it's worth removing. Achieving parity w/audits 1.0 meant being able to list each rule selector in devtools (which is where this originally came from), so if it's needed there it will be easier? If we think we're memory constrained I'll remove though.

@patrickhulce
Copy link
Collaborator Author

PTAL :)

@brendankenny
Copy link
Member

Just thought of a last thing. We should really make a checklist of things new gatherers/audits need...

Will you add a smokehouse expectation for this? Since this isn't explicitly DBW, adding it to offline-expectations.js may make the most sense, though the DBW test page will do a better job putting the gatherer and audit through their paces.

@@ -37,6 +37,10 @@ function filterStylesheetsByUsage(stylesheets, propName, propVal) {
const deepClone = stylesheets.map(sheet => Object.assign({}, sheet));

return deepClone.filter(s => {
if (s.isDuplicate) {
Copy link
Contributor

Choose a reason for hiding this comment

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

thanks 👍

@@ -124,10 +124,12 @@ class UnusedCSSRules extends Audit {

const indexedSheets = UnusedCSSRules.indexStylesheetsById(styles);
const unused = UnusedCSSRules.countUnusedRules(usage, indexedSheets);
const unusedRatio = (unused / usage.length) || 0;
Copy link
Member

Choose a reason for hiding this comment

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

yay smoke tests :)

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!

@brendankenny brendankenny merged commit bbaff25 into master Jan 11, 2017
@brendankenny brendankenny deleted the css_usage branch January 11, 2017 19:46
andrewrota pushed a commit to andrewrota/lighthouse that referenced this pull request Jan 13, 2017
Adds basic CSS rule usage tracking gatherer and audit. Moves toward parity with Audits 1.0 as per GoogleChrome#909.
@XhmikosR
Copy link
Contributor

XhmikosR commented Jan 14, 2017

This is causing runtime errors for me. Can someone have a look?

C:\Users\xmr\Desktop\lighthouse>node lighthouse-cli\index.js --output=pretty https://xhmikosr.github.io/greek-vat-calculator/
  Lighthouse CLI Launching Chrome... +0ms
  ChromeLauncher Waiting for browser. +0ms
  ChromeLauncher Waiting for browser... +0ms
  ChromeLauncher Waiting for browser...√ +515ms
  status Initializing… +1s
  status Loading page & waiting for onload URL, HTTPS, Viewport, ThemeColor, Manifest, Accessibility, ContentWidth +360ms
  statusEnd Loading page & waiting for onload +2s
  status Retrieving trace +0ms
  status Retrieving network records +141ms
  status Retrieving: URL +0ms
  status Retrieving: HTTPS +0ms
  status Retrieving: Viewport +15ms
  status Retrieving: ThemeColor +0ms
  status Retrieving: Manifest +0ms
  status Retrieving: Accessibility +0ms
  status Retrieving: ContentWidth +87ms
  status Loading page & waiting for onload ServiceWorker, Offline +342ms
  statusEnd Loading page & waiting for onload +569ms
  status Retrieving network records +1ms
  status Retrieving: ServiceWorker +2ms
  status Retrieving: Offline +1ms
  status Loading page & waiting for onload HTTPRedirect, HTMLWithoutJavaScript +327ms
  statusEnd Loading page & waiting for onload +1s
  status Retrieving network records +1ms
  status Retrieving: HTTPRedirect +2ms
  status Retrieving: HTMLWithoutJavaScript +5ms
  method <= browser ERR:error CSS.startRuleUsageTracking  +327ms
  status Disconnecting from browser... +0ms
  ChromeLauncher Killing all Chrome Instances +0ms
Runtime error encountered: Error: Protocol error (CSS.startRuleUsageTracking): 'CSS.startRuleUsageTracking' wasn't found
    at CriConnection.handleRawError (C:\Users\xmr\Desktop\lighthouse\lighthouse-core\gather\connections\connection.js:134:11)
    at callback.resolve.Promise.resolve.then._ (C:\Users\xmr\Desktop\lighthouse\lighthouse-core\gather\connections\connection.js:103:23)
Error: Protocol error (CSS.startRuleUsageTracking): 'CSS.startRuleUsageTracking' wasn't found
    at CriConnection.handleRawError (C:\Users\xmr\Desktop\lighthouse\lighthouse-core\gather\connections\connection.js:134:11)
    at callback.resolve.Promise.resolve.then._ (C:\Users\xmr\Desktop\lighthouse\lighthouse-core\gather\connections\connection.js:103:23)

@nathanjd
Copy link

I am having the same runtime errors as @XhmikosR.

commit 97d48d3
OS X 10.11.3
node v6.9.4

Performed npm installs around 1-16-2017 @ 3pm.

q4i2@M00973344 ~/Projects/web-perf/lighthouse (master)
$ node lighthouse-cli --disable-device-emulation --disable-cpu-throttling https://shop.nordstrom.com
  status Initializing… +0ms
  status Loading page & waiting for onload URL, HTTPS, Viewport, ThemeColor, Manifest, Accessibility, ContentWidth +455ms
  statusEnd Loading page & waiting for onload +16s
  status Retrieving trace +2ms
  status Retrieving network records +3s
  status Retrieving: URL +1ms
  status Retrieving: HTTPS +0ms
  status Retrieving: Viewport +6ms
  status Retrieving: ThemeColor +34ms
  status Retrieving: Manifest +10ms
  status Retrieving: Accessibility +3ms
  status Retrieving: ContentWidth +3s
  status Loading page & waiting for onload ServiceWorker, Offline +530ms
  statusEnd Loading page & waiting for onload +1s
  status Retrieving network records +4ms
  status Retrieving: ServiceWorker +0ms
  status Retrieving: Offline +0ms
  status Loading page & waiting for onload HTTPRedirect, HTMLWithoutJavaScript +376ms
  statusEnd Loading page & waiting for onload +4s
  status Retrieving network records +0ms
  status Retrieving: HTTPRedirect +0ms
  status Retrieving: HTMLWithoutJavaScript +5ms
  method <= browser ERR:error CSS.startRuleUsageTracking  +471ms
  status Disconnecting from browser... +1ms
Runtime error encountered: Error: Protocol error (CSS.startRuleUsageTracking): 'CSS.startRuleUsageTracking' wasn't found
    at CriConnection.handleRawError (/Users/q4i2/Projects/web-perf/lighthouse/lighthouse-core/gather/connections/connection.js:134:11)
    at callback.resolve.Promise.resolve.then._ (/Users/q4i2/Projects/web-perf/lighthouse/lighthouse-core/gather/connections/connection.js:103:23)
Error: Protocol error (CSS.startRuleUsageTracking): 'CSS.startRuleUsageTracking' wasn't found
    at CriConnection.handleRawError (/Users/q4i2/Projects/web-perf/lighthouse/lighthouse-core/gather/connections/connection.js:134:11)
    at callback.resolve.Promise.resolve.then._ (/Users/q4i2/Projects/web-perf/lighthouse/lighthouse-core/gather/connections/connection.js:103:23)

@patrickhulce
Copy link
Collaborator Author

thanks for the report @XhmikosR and @nathanjd! you can follow along on #1473

@XhmikosR
Copy link
Contributor

@patrickhulce: I figured out that must be the reason since Travis is passing. Thanks for the info!

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.

None yet

6 participants