cypress: File watching does not execute actual code changes when site under test utilizes Service Workers.

Related to #686.

It’s possible the application under test ends up using Server Workers to inadvertently cache the files Cypress uses to serve spec files. This prevents changes to the spec files from ever being updated.

Prior to the beginning of all tests we should clear the Service Worker cache and then add a new cy command and add this to lifecycle rules cy.clearServiceWorkerCache().

We could make this command print out all the cache keys it cleared.

Workaround:

before(function () {
  // run this once before all code
  return window.caches.keys().then(function(cacheNames) {
    return Promise.all(
      cacheNames.map(function(cacheName) {
        return window.caches.delete(cacheName);
      })
    );
  })
})

About this issue

  • Original URL
  • State: open
  • Created 7 years ago
  • Reactions: 20
  • Comments: 35 (6 by maintainers)

Commits related to this issue

Most upvoted comments

@mackelito I’ve managed to unregister all service workers before each test in one of my specs like this:

beforeEach(() => {
  if (window.navigator && navigator.serviceWorker) {
    navigator.serviceWorker.getRegistrations()
    .then((registrations) => {
      registrations.forEach((registration) => {
        registration.unregister()
      })
    })
  }
})

We need to make a decision here. Currently ServiceWorkers end up generating very confusing behavior in Cypress which is hard for new (and even old) users to debug.

Proposal

  1. Prevent ServiceWorkers from ever being generated by default.
  2. Add two new commands above to enable clearing cache + registrations before each test + after all the tests finish
  3. Add a new config property: enableServiceWorkers: false by default (?)

When we enable lifecycle events this could potentially conflict…

Prevent ServiceWorkers from ever being generated by default.

I want to be able to e2e test my serviceworker’s behavior, so I’d hate to see this happen. I’d love to see an API to clear the caches before each test run though!

We could even indefinitely prevent service workers from ever being registered by doing:

cy.visit("https://testy.gift/", {
  onBeforeLoad: function(win) {
    const promise = new Promise(function(resolve) {});
    return win.navigator.serviceWorker.register = function() {
      return promise;
    }
  }
})

Another potential lifecycle command we could add at the beginning of each test, and then subsequently at the end of all tests is something like: cy.clearServiceWorkerRegistrations()

The implementation for that might look something like:

window.navigator.serviceWorker.getRegistrations()
.then(function(registrations) {
  return Promise.map(registrations, function(registration) {
    return registration.unregister()
  })
})

For me the solution was to check in the UI code if Cypress is running, and if yes, i just unregister the service worker, like this:

if (window.Cypress) {
    serviceWorker.unregister()
} else {
    serviceWorker.register()
}

I’ve disabled SW with the following snippet:

Cypress.on('window:before:load', win => {
  // disable service workers
  delete win.navigator.__proto__.serviceWorker;
});

source

I solved the issues I was having by updating my navigation fallback config in sw-precache. This is what I added:

navigateFallbackWhitelist: [/^\/[^_]+$/], // fallback for anything that doesn't start with /__ which is used by cypress

Additionally seeing problems with ServiceWorkers caching the /__ root client path which prevents Cypress from being served entirely.

Nevermind, I seem to have fixed my own issue by serving to localhost instead of 0.0.0.0 (Chrome seems to only be able to allow service workers over HTTP when they come from localhost).

Prevent ServiceWorkers from ever being generated by default.

Using ServiceWorkers for caching is only one use case scenario. They are effectively built-in proxy servers and we are using SW for example to add auth header for all outgoing requests and not using SW for caching at all.

Just my 2c.