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

Service Workers can break Cypress #16192

Closed
csvan opened this issue Apr 25, 2021 · 9 comments
Closed

Service Workers can break Cypress #16192

csvan opened this issue Apr 25, 2021 · 9 comments
Labels
existing workaround prevent-stale mark an issue so it is ignored by stale[bot]

Comments

@csvan
Copy link

csvan commented Apr 25, 2021

Current behavior

Navigating to a page which uses a service worker can in some cases break Cypress completely, causing the Cypress panel to disappear and the page to stop responding to Cypress commands.

I have isolated this to the presence of the service worker - disabling it causes the page to behave normally under Cypress.

Desired behavior

Cypress should be able to reliably test all pages using service workers.

Test code to reproduce

To reproduce, I have set up an example URL which reliably reproduces this behavior when visited by Cypress. All that is needed to reproduce is a simple Cypress test which runs the following:

cy.visit('https://qa.elegantconnect.com');

Versions

Cypress 6.9.1, Nodejs 16.0.0, Chrome 90, OSX Big Sur.

Other

The service worker in the provided example is created using Google Workbox (https://developers.google.com/web/tools/workbox). Below are the parts of it which most likely cause the behavior by defining a default URL, but I do not understand fully how this affects Cypress:

import { registerRoute, NavigationRoute } from 'workbox-routing';
import { precacheAndRoute, createHandlerBoundToURL } from 'workbox-precaching';

// eslint-disable-next-line no-underscore-dangle
precacheAndRoute(self.__WB_MANIFEST, {
  ignoreURLParametersMatching: [/.*/],
  directoryIndex: 'appshell/index.html',
});

const handler = createHandlerBoundToURL('/appshell/index.html');
const navigationRoute = new NavigationRoute(handler);
registerRoute(navigationRoute);
@jennifer-shehane
Copy link
Member

Can you include a full failing example? The example given with just the cy.visit() does load the page and looks fine within Cypress as far as I am aware. Thanks.

Screen Shot 2021-04-26 at 12 08 28 PM

@jennifer-shehane jennifer-shehane added the stage: needs information Not enough info to reproduce the issue label Apr 26, 2021
@csvan
Copy link
Author

csvan commented May 2, 2021

@jennifer-shehane I see, that's very strange. I can reliably reproduce it, but it might be a consequence of network infra on our side. I will close this until someone else can reproduce.

@csvan csvan closed this as completed May 2, 2021
@jennifer-shehane jennifer-shehane removed the stage: needs information Not enough info to reproduce the issue label May 3, 2021
@fhp
Copy link

fhp commented Jun 29, 2021

I can also reproduce this, and I have the same issue while testing my website (https://www.gynzykids.com).

It does not break immediately, only after the service worker has finished all the preloading. So rerunning the test after a minute or so would break the test.

As a workaround, I found that setting the Bypass for network option in the service worker webdev tab fixes the issue.
image

Another workaround I found is this, which disables the service worker:

cy.visit('https://www.gynzykids.com', {
    onBeforeLoad (win) {
        delete win.navigator.__proto__.serviceWorker;
    }
});

However the tests would be better if they also test the behavior of the service worker.

@csvan
Copy link
Author

csvan commented Dec 13, 2021

We managed to get Cypress to work with Service Workers using Workbox. Key is simply to tell the SW not to cache (or precache) any Cypress resources:

const navigationRoute = new NavigationRoute(handler, {
  denylist: [
    new RegExp('/__'),
    new RegExp('cypress'),
  ],
});

Then add a rule which forces all Cypress requests to be network-only:

  registerRoute(
    (request) => request.url.includes('cypress'),
    new NetworkOnly(),
  );

@csvan
Copy link
Author

csvan commented Dec 13, 2021

@jennifer-shehane it hit me now that the reason you did not see it is probably because the Service Worker did not have time to install, I noticed it took quite a while to prefetch all resources.

However, as indicated above we have made it work now, but it feels hackish and is WorkBox specific.

@cypress-app-bot
Copy link
Collaborator

This issue has not had any activity in 180 days. Cypress evolves quickly and the reported behavior should be tested on the latest version of Cypress to verify the behavior is still occurring. It will be closed in 14 days if no updates are provided.

@cypress-app-bot cypress-app-bot added the stale no activity on this issue for a long period label May 17, 2023
@cypress-app-bot cypress-app-bot closed this as not planned Won't fix, can't repro, duplicate, stale May 31, 2023
@OtacilioN
Copy link

OtacilioN commented Aug 9, 2023

For me, the workaround that worked was:

beforeEach(() => {
    cy.visit("https://appdev.aprendizap.com.br", {
      onBeforeLoad(win) {
        delete win.navigator.__proto__.ServiceWorker;
        delete win.navigator.serviceWorker;
      },
    });
    if (window.navigator && navigator.serviceWorker) {
      navigator.serviceWorker.getRegistrations().then((registrations) => {
        registrations.forEach((registration) => {
          registration.unregister();
        });
      });
    }
  });

I also added the registration.unregister to afterEach, and had to unregister all service works manually for the first time in application tab.

the full project can be found at: https://github.com/Fundacao-1Bi/e2e-Aprendizap-web

@pauloh-sm
Copy link

For me, the workaround that worked was:

beforeEach(() => {
    cy.visit("https://appdev.aprendizap.com.br", {
      onBeforeLoad(win) {
        delete win.navigator.__proto__.ServiceWorker;
        delete win.navigator.serviceWorker;
      },
    });
    if (window.navigator && navigator.serviceWorker) {
      navigator.serviceWorker.getRegistrations().then((registrations) => {
        registrations.forEach((registration) => {
          registration.unregister();
        });
      });
    }
  });

I also added the registration.unregister to afterEach, and had to unregister all service works manually for the first time in application tab.

the full project can be found at: https://github.com/Fundacao-1Bi/e2e-Aprendizap-web

Work for me. Thanks!!

@suterma
Copy link

suterma commented Oct 24, 2024

I also have success using the unregistration, like described above. I have just put it in a global before each, and it's working great!

@jennifer-shehane jennifer-shehane added prevent-stale mark an issue so it is ignored by stale[bot] existing workaround and removed stale no activity on this issue for a long period labels Oct 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
existing workaround prevent-stale mark an issue so it is ignored by stale[bot]
Projects
None yet
Development

No branches or pull requests

7 participants