Skip to content

Instantly share code, notes, and snippets.

@yinonc
Last active February 7, 2019 14:17
Show Gist options
  • Save yinonc/f83d6ffd836e5e5253aba698556a2f14 to your computer and use it in GitHub Desktop.
Save yinonc/f83d6ffd836e5e5253aba698556a2f14 to your computer and use it in GitHub Desktop.
Lighthouse basics

Lighthouse

All the details are from Lighthouse conference guide (at the README file)

  1. Introduction
  2. Base Audits
  3. Getting Started
  4. Writing Your Own Audit
  5. Using Lighthouse Programmatically
  6. Testing with Lighthouse example
  7. Continues Integrations

Introduction

Automated checks for Progressive Web Apps.

Base Audits

  1. PWA checklist: (highlights).
  • Loading time for 3G
  • Using service worker
  • Respond with a 200 when offline (should we do that?)
  • Prompting to users to add the app to their home-screen
  • Configure for a custom splash screen
  • Fetch a Manifest
  • Size correctly for the viewport
  • Redirect HTTP traffic to HTTPS
  1. Best Practices: (highlights)
  • Using HTTP/2 for all of its resources
  • Using passive listeners to improve scrolling performance
  • Don't use document.write()
  • Not logging to console browser errors
  1. Performance measuring: (highlights).
    Divides to real user experiences:
  • FP - First Paint - the first non-blank paint on screen
  • FCP - First Contentful Paint - Navigation has successfully started
  • FMP - First Meaningful Paint - Page's primary content is visible
  • TTI - Time To Interactive - Visually usable and engagable
    Moreover, more indications for performance increasing by minified javascript files for example.
  1. Accessibility: (highlights)
  • Unique id attributes
  • Page specifies valid language (<html> element should have a lang attribute)
  • <frame> and <iframe> elements should have a title
  • Buttons should have an accessible names

Getting Started

installation with: npm i -g lighthouse

Run with: lighthouse http://localhost:8080
Useful CLI flags:

  • --view For the chrome dev-tools audits tab
  • --output choose output scheme ('json' / 'html' / 'csv' - default 'html') --output=json
  • --output-path output to file --output-path=./report.json
  • --extra-headers set extra HTTP headers to pass with request --extra-headers=./path/to/file.json
    Issue with solution for extra headers: GoogleChrome/lighthouse#5085
  • --emulated-form-factor device form factor. default for mobile. --emulated-form-factor=desktop

Writing your own audit

You can write your own lighthouse plugin: Assume you have set a global variable like siteMetrics, with attributes on it.
For example, interactiveTime and you've set it to X time after your content is loaded.

  1. Create a Gatherer to collect info
const Gatherer = require('lighthouse').Gatherer

class SiteMetricsGatherer extents Gatherer {
  beforePass() {
    // before page loads
  }

  afterPass(options) {
    const driver = options.driver
    return driver.evaluateAsync('window.siteMetrics') // Runs this javascript snippet in the context of the page
  }
}

module.exports = SiteMetricsGatherer
  1. Create an Audit to verify metrics
const Audit = require('lighthouse').Audit

class LoadAudit extents Audit {
  static get meta() {
    return {
      category: 'CustomThing',
      name: 'load-audit',
      description: 'Site is loaded and ready',
      requiredArtifacts: ['SiteMetricsGatherer']
    }
  }
  static audit(artifacts) {
    const interactiveTime = artifacts.SiteMetricsGatherer.interactiveTime
    return {
      rawValue: `${interactiveTime} ms`,
      score: interactiveTime <= 4000 //your threshold
    }
  }
}

module.exports = LoadAudit
  1. Create a Config to run them

custom-config.js:

module.exports = {
  extends: 'lighthouse:default', // take all the default from lighthouse
  passes: [{
    passName: 'defaultPass',
    gatherers: ['site-metrics-gatherer']
  }],
  audits: ['load-audit'],
  categories: { // category in dev-tools audits tab
    mysite: {
      name: 'My site metrics',
      description: 'Metrics for our site',
      audits: [{id: 'load-audit', weight: 1}]
    }
  }
}

When you've finished, just run audits, and your new category would appear at Audits!

If you would like to run only your custom audit, run:

lighthouse \ --config-path=custom-config.js \ http://localhost:8080 --view

Using Lighthouse Programmatically

You can view the whole guide here.

const Lighthouse = require('lighthouse')
const ChromeLauncher = require('chrome-launcher')

async function launchChromeAndRunLighthouse(url, flags, config = null) {
  const chrome = await ChromeLauncher.launch()
  flags.post = chrome.exports
  const results = await Lighthouse(url, flags, config)

  await chrome.kill()
  return results
}

launchChromeAndRunLighthouse('https://example.com', {output: 'json'})
  .then(results => {
    // use results
  })

Testing with Lighthouse example

Example of my own testing for a lighthouse scoring by categories:

Config file:

module.exports = {
    CATEGORIES: {
        pwa: {
            CURRENT_SCORE: 0.2
        },
        accessibility: {
            CURRENT_SCORE: 0.5
        },
        performance: {
            CURRENT_SCORE: 0.15
        },
        seo: {
            CURRENT_SCORE: 0.6
        }
    },
    URL: 'https://example.com'
}

Test file:

'use strict'

const _ = require('lodash')
const lighthouse = require('lighthouse')
const chromeLauncher = require('chrome-launcher')
const lighthouseMeasuring = require('./lighthouseMeasuringConfig')

function launchChromeAndRunLighthouse(url, opts, config = null) {
    return chromeLauncher.launch({chromeFlags: opts.chromeFlags}).then(chrome => {
        opts.port = chrome.port
        return lighthouse(url, opts, config).then(results => chrome.kill().then(() => results.lhr))
    })
}

describe('lighthouse', function () {
    let results = null

    beforeAll(function () {
        this.origTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL
        jasmine.DEFAULT_TIMEOUT_INTERVAL = 30 * 1000
    })

    afterAll(function () {
        jasmine.DEFAULT_TIMEOUT_INTERVAL = this.origTimeout
    })

    describe('measuring blank template', function () {
        beforeAll(async function () {
            results = await launchChromeAndRunLighthouse(lighthouseMeasuring.URL, {
                output: 'json',
                extraHeaders: lighthouseMeasuring.SESSION_HEADER,
                chromeFlags: ['--headless'],
                emulatedFormFactor: 'desktop'
            })
        })

        _.forOwn(lighthouseMeasuring.CATEGORIES, (category, categoryName) => {
            describe(`${category} score`, function () {
                it('should be bigger than current score', async () => {
                    const score = results.categories[categoryName].score

                    expect(score).not.toBeLessThan(category.CURRENT_SCORE)
                })
            })
        })
    })
})

Continues Integrations

The example is used with Travis:

language: node_js
script:
- npm run lint
- npm run build
after_success:
- './travis/deploy_pr_gae.sh' // will vary based on your setup
- export LH_MIN_PASS_SCORE=96
- export LH_TEST_URL=https://your.staging.server.com/
- node travis/runLighthouse.js $LH_TEST_URL $LH_MIN_PASS_SCORE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment