Back to Repositories

Testing Service Worker Prefetch Functionality in GatsbyJS

This test suite validates the service worker prefetching behavior in Gatsby’s offline plugin. It specifically examines how the plugin handles different types of resource links and ensures correct prefetch functionality for performance optimization.

Test Coverage Overview

The test suite provides comprehensive coverage of the service worker’s prefetching mechanism.

Key areas tested include:
  • Handling of preconnect, prefetch, and prerender link types
  • Script and stylesheet resource management
  • Selective prefetching behavior for different resource types
  • DOM manipulation verification for added prefetch links

Implementation Analysis

The testing approach utilizes Jest’s JSDOM environment to simulate browser behavior and DOM manipulation. It employs a systematic verification pattern for link elements and their attributes, focusing on the onServiceWorkerActive handler’s functionality.

Technical implementation includes:
  • Dynamic head element creation and manipulation
  • Service worker state simulation
  • Resource URL management testing

Technical Details

Testing tools and configuration:
  • Jest test framework with JSDOM environment
  • Mock implementation of addHeadElement utility
  • ServiceWorker API simulation
  • DOM query selectors for verification
  • Expect assertions for attribute checking

Best Practices Demonstrated

The test demonstrates several quality testing practices for browser-based functionality.

Notable practices include:
  • Isolation of DOM manipulation logic
  • Comprehensive attribute verification
  • Clear test case organization
  • Explicit resource type handling
  • Efficient setup and teardown management

gatsbyjs/gatsby

packages/gatsby-plugin-offline/src/__tests__/gatsby-browser.test.js

            
/**
 * @jest-environment jsdom
 */

const { onServiceWorkerActive } = require(`../gatsby-browser`)

it(`does not add prefetch for preconnect/prefetch/prerender`, () => {
  const addHeadElement = (tagName, attributes) => {
    const el = document.createElement(tagName)
    for (const key in attributes) {
      el.setAttribute(key, attributes[key])
    }
    document.head.appendChild(el)
    return el
  }

  // Should not be prefetched
  addHeadElement(`link`, { rel: `preconnect`, href: `https://gatsbyjs.com` })
  addHeadElement(`link`, { rel: `prefetch`, href: `https://gatsbyjs.com` })
  addHeadElement(`link`, { rel: `prerender`, href: `https://gatsbyjs.com` })
  addHeadElement(`script`, { src: `https://gats.by/script.js` })
  addHeadElement(`style`, { "data-href": `https://gats.by/lazy.css` })
  addHeadElement(`link`, {
    rel: `stylesheet`,
    href: `https://gats.by/style.css`,
  })
  addHeadElement(`link`, {
    rel: `preconnect dns-prefetch`,
    href: `https://www.google-analytics.com`,
  })

  onServiceWorkerActive({
    getResourceURLsForPathname: () => [],
    serviceWorker: { active: {} },
  })

  // First five link elements should be the actual resources added above
  // The remaining link elements should be the one added by gatsby-browser as prefetch
  const links = [].slice.call(document.querySelectorAll(`head > link`))

  // Should add prefetch for stylesheets, scripts and lazy-loaded styles
  expect(links[5].href).toBe(`https://gats.by/script.js`)
  expect(links[5].rel).toBe(`prefetch`)
  expect(links[6].href).toBe(`https://gats.by/lazy.css`)
  expect(links[6].rel).toBe(`prefetch`)
  expect(links[7].href).toBe(`https://gats.by/style.css`)
  expect(links[7].rel).toBe(`prefetch`)
})