question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Question] How can I trigger a service worker update by modifying the HTTP response?

See original GitHub issue

I am working on an SPA that I recently added a service worker to, which pre-caches the application shell using a cache-first strategy and caches navigation requests using a network -first strategy.

The SPA contains some logic that displays a “A new version is available!” banner with a “Reload” button at the top of the page when it detects a “waiting” service worker. I would like to write an end-to-end test for this logic and the logic behind the “Reload” button but cannot figure out how to trigger a service worker update in Playwright.

The approach that I have been playing with is modifying the service worker response after the service worker has been installed and a page reload has been triggered. I can modify the response and its response headers but it is not triggering an update event in the browser.

Here is the test code:

test("shows an update button when the service worker file has changed", async ({
  page,
  context
}, testInfo) => {
  const newScriptBytes = "/*test*/"
  const timeout = testInfo.timeout
  await page.goto("/")
  await waitForReadyServiceWorker({ context, page, timeout })

  // change the bytes of all additional service worker requests
  await page.route("/service-worker.js", async route => {
    const serverResponse = await page.request.fetch(serviceWorkerUrl, {
      ignoreHTTPSErrors: true
    })
    let serviceWorkerScript = await serverResponse.text()
    serviceWorkerScript = serviceWorkerScript.concat(newScriptBytes)
    await route.fulfill({
      response: serverResponse,
      body: serviceWorkerScript,
      headers: {
        ...serverResponse.headers(),
        "Etag": "v1",
        "Last-Modified": new Date().toUTCString(),
        "Content-Length": serviceWorkerScript.length.toString()
      }
    })
  })

  await page.reload()

  await expect(
    page.locator(`text=A new version is available!`)
  ).toBeVisible()
})

export async function waitForReadyServiceWorker(args: {
  context: BrowserContext
  page: Page
  timeout?: number
}): Promise<Worker | undefined> {
  const timeoutId = createOperationTimeout(args.timeout)
  const url = await args.page.evaluate(async () => {
    const readySw = await navigator.serviceWorker.ready
    return readySw.active.scriptURL
  })
  clearTimeout(timeoutId)
  return args.context.serviceWorkers().find(worker => worker.url() === url)
}

Here is the initial service worker response

initial service worker response

Here is the modified service worker response

modified service worker response

stackoverflow post

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:8 (7 by maintainers)

github_iconTop GitHub Comments

3reactions
pavelfeldmancommented, Jun 8, 2022

Intercepting the service-worker.js request is not yet implemented in Playwright. We are almost there, but it needs some final touches. I’ll let @rwoll comment on when it becomes available.

The rest of your test seems to be ok. You’ll reload the page, it’ll re-request service worker, then sw will load and your page will be able to detect a new sw. You could also do something like reg.update here that would theoretically check for the new sw w/o reloading. It requires you to modify the SPA though to store registration pointer, but avoids artificial reload. Does this all make sense?

1reaction
pavelfeldmancommented, Jun 8, 2022

However, I don’t see how it has anything to do with intercepting and modifying the response for the service-worker.js script request, which, in my case, would allow me to emulate a service-worker.js script change on the server so I can trigger a service worker “update” in the client

Yes, this set of changes does intercept the main request as well!

Read more comments on GitHub >

github_iconTop Results From Across the Web

How can I trigger a service worker update in Playwright by ...
I can modify the response and its response headers but it is not triggering an update event in the browser. Here is the...
Read more >
ServiceWorkerRegistration.update() - Web APIs | MDN
The update() method of the ServiceWorkerRegistration interface attempts to update the service worker. It fetches the worker's script URL, ...
Read more >
Handling Service Worker updates – how to keep the app ...
The update problem ... Service Worker is a piece of JavaScript that works as a single controller & network proxy for all the...
Read more >
Chapter 4. Intercepting network requests - Progressive Web ...
Using Service Workers, you can build your own custom HTTP responses, including editing their headers. This functionality makes Service Workers extremely ...
Read more >
Angular service worker introduction
Service workers function as a network proxy. They intercept all outgoing HTTP requests made by the application and can choose how to respond...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found