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

Simulate offline mode #235

Open
bahmutov opened this issue Sep 22, 2016 · 67 comments
Open

Simulate offline mode #235

bahmutov opened this issue Sep 22, 2016 · 67 comments
Labels
existing workaround type: feature New feature that does not currently exist

Comments

@bahmutov
Copy link
Contributor

Description
Offline support is becoming important to web applications. It would be nice to switch the browser to the offline mode from Cypress either by test or for all tests and run it. If might be difficult and might require Cypress to handle ServiceWorkers very well.

Additional Info

@brian-mann
Copy link
Member

brian-mann commented Sep 22, 2016

This should be possible to do, but it's not immediately obvious or easy.

Thoughts / Concerns

  • To date we haven't built in any specific API's that only work with one browser vendor. I'd like to try to find another way to do this, perhaps at OS network or proxy level since we control those two things.
  • Chrome supports this out of the box in the debugger protocol but attempting to use that puts us in the same situation as issuing native events - that is you cannot have the Dev Tools open and an open connection to the debugger protocol. Chromium team is working on multiplexing support so we're kind of waiting for that to drop before adding support.
  • If we did write API's exclusive to a single browser vendor, would they automatically throw (or be ignored) when running on a different browser? We'd also need a way at the test or suite level to indicate you want to inclusively or exclusively run tests based on the current browser.
  • If we did use Chrome's APIs to achieve this, it would essentially break Cypress's ability to do automation since websocket events would no longer be able to reach the server. So if we did tap into Chrome we could only partially throttle and not fully force into offline mode. Another option would be to use the WebRequest API's and whitelist Cypress network traffic but throttle or reject others.

I'll move this into our Roadmap for further research.

@brian-mann brian-mann added the type: feature New feature that does not currently exist label Sep 22, 2016
@jennifer-shehane jennifer-shehane added the stage: proposal 💡 No work has been done of this issue label Oct 27, 2017
@jennifer-shehane
Copy link
Member

With Chrome 63 coming out we will be able to support the debugger protocol. #832

@bbsimonbb
Copy link

Good onya guys. I'm ready for this right now. You know what to do.

@juristr
Copy link

juristr commented May 14, 2018

Is there any progress/news on this? Would be cool to be able to test PWAs 👍

@jennifer-shehane
Copy link
Member

No progress on this unfortunately.

@maku-zuhlke
Copy link

maku-zuhlke commented Oct 19, 2018

I am using Redux and we store the online/offline status of the browser there. So what we did was to expose the Redux store like this in eg index.js (only works on localhost):

if (window.Cypress) {
    window.store = store
}

Then in a test in cypress we dispatched an action to set the Redux state to be offline:

cy.window().its('store')
  .then(
    store => store.dispatch({type: 'OFFLINE', status: true})
  );

@lolpez
Copy link

lolpez commented Apr 25, 2019

Guys it's 2019 👀

@mhrisse

This comment has been minimized.

@jennifer-shehane
Copy link
Member

Related issue for clearing ServiceWorkers: #702

@lolpez We prioritize issues we work on based on a variety of factors - including the number of users requesting/helped by a feature. While this is still on our Roadmap, many other features are a higher priority today as a result of this assessment. Sorry for the delay.

@gr2m
Copy link

gr2m commented Dec 4, 2019

I tried to simulate offline by setting up a reverse proxy to my running server and then shutting it down.

const httpProxy = require("http-proxy");

context("Offline", () => {
  before(() => {
    httpProxy.createProxyServer({ target: "http://localhost:9000" }).listen(9001);

    cy.log("Open / on proxy");
    cy.visit("http://localhost:9001/");
  });

  it("should show update notification", () => {
    // wait for initial service worker installation
    cy.wait(3000);

    // stop the proxy server
    proxy.close();

    // reload the page
    cy.reload();
  });
});

But I get a http.createServer is not a function error.

If I run just these two files in a separate file there is no problem

const httpProxy = require("http-proxy");
httpProxy.createProxyServer({ target: "http://localhost:9000" }).listen(9001);

Is this a known limitation / problem?

@barnabynorman
Copy link

It would be really good to have this feature.

I can currently set the offline state in our vuex store but when changing pages (ie cy.visit) the application rechecks the state and updates the store value and is flushing queues before I get a chance to set the store again!

@miroslavvojtus
Copy link

Absence of this feature actually forces us to use selenium with direct access to driver api so we can test our core functionality in offline mode as our use case is offline-first.

I hope you will find time soon for this.

@Ozzyjtjustin
Copy link

The project my team is working on requires that we are able to simulate a No Internet Connection environment as well.

Please provide an update on this!

@jpita
Copy link

jpita commented Feb 14, 2020

hello, any updates on this?
is it on the roadmap?

@jennifer-shehane
Copy link
Member

This issue is still in the 'proposal' stage, which means no work has been done on this issue as of today, so we do not have an estimate on when this will be delivered.

@shubhsherl
Copy link

shubhsherl commented Mar 26, 2020

One way of simulating offline mode:

cy.server({ force404: true }); //  offline mode

cy.server({ enable: false});  // online mode

https://docs.cypress.io/api/commands/server.html

@jpita
Copy link

jpita commented Mar 26, 2020

One way of simulating offline mode:

cy.server({ force404: true }); //  offline mode

cy.server({ enable: false});  // online mode

https://docs.cypress.io/api/commands/server.html

that only blocks the calls, it doesn't tell the browser there's no internet

@shubhsherl
Copy link

shubhsherl commented Mar 27, 2020

that only blocks the calls, it doesn't tell the browser there's no internet

Yes, it doesn't tell the browser that there is no internet, but by preventing the requests, we can test the offline functionality of PWA. It's just a workaround.

@jpita
Copy link

jpita commented Mar 27, 2020

In my case it doesn't help, it should appear a "offline" banner and it doesn't.
Thanks anyway

@EtienneBruines
Copy link

EtienneBruines commented Apr 3, 2020

The root of the app is not cachable because of the MITM that Cypress performs (`document.domain = 'localhost').

This would make it (at least to me) that one would enable offline-mode after having started testing. First you'd want to install your service worker and such, adding all required files to the cache, and then run some tests. This would mean a global option is not required/desired - if you have no cache, no prior visits to the site - offline will never work anyways.

Design

// Quick and dirty
cy.offline()
// - do tests inbetween
cy.online()

// A probably better way:
cy.network({offline: true})
// - do tests inbetween
cy.network({offline: false])

The latter would enable Cypress to at at later point also add throttling in the options. Not sure as to the usefulness of throttling, but that's point of discussion for another issue.

Docs

The Chrome Debugger (Chrome, Electron) allows for the following, but Firefox does not.

    Cypress.automation('remote:debugger:protocol', {
        command: 'Network.enable',
      })

      Cypress.automation('remote:debugger:protocol', {
        command: 'Network.emulateNetworkConditions',
        params: {
          offline: options.offline,
          'latency': 0,
          'downloadThroughput': 0,
          'uploadThroughput': 0,
          'connectionType': 'none',
        },
      })

Hurdles

@brian-mann wrote:
If we did use Chrome's APIs to achieve this, it would essentially break Cypress's ability to do automation since websocket events would no longer be able to reach the server. So if we did tap into Chrome we could only partially throttle and not fully force into offline mode. Another option would be to use the WebRequest API's and whitelist Cypress network traffic but throttle or reject others.

Half-way through my test (within a single it), I was able to enable offline-mode without any problems (manually, that is). After the tasks have been queued at the start, there should not be any issues with that.

Update

I've created a PR, but I'm not making any progress on Firefox.

According to this issue Firefox does not seem to implement Network.emulateNetworkConditions yet - and I haven't found a suitable replacement either. For some reason, those APIs don't have easily accessible documentation.

The Firefox UI has a Work offline mode, which does the same - I just haven't found a way to automate this.

Update 2

Unless we figure out how to automate it for Firefox, the chances of any PR making it into Cypress are slim to none.

Call to everyone: feel free to try and find a way ;-).

Update 3

Work on the Network.emulateNetworkConditions CDP command in Firefox has started. All seems to be going well, except that Firefox - as of writing- , allows access to everything that resolves to localhost even in Work Offline-mode. This is still being figured out to make automated testing a bit more useful.

@cypress-bot cypress-bot bot added stage: work in progress stage: ready for work The issue is reproducible and in scope and removed stage: proposal 💡 No work has been done of this issue stage: work in progress labels Apr 3, 2020
@archfz
Copy link

archfz commented Jan 6, 2022

@lopezc184 I was able to reproduce the issue, only on chrome and edge. This doesn't seem to be an issue with cypress-terminal-report plugin. For example if you uninstall the plugin and add the following to your code, you will still reproduce the issue:

plugins.js

module.exports = (on) => {
  on('task', {
    'mytask': () => console.log('lol'),
  });
};

end of your spec (the same code as you have above)

   // ...
   cy.wrap(null).then(() => {
      goOnline()
      assertOnline()
      cy.get('some element').should('be.visible')
    })
   cy.task('mytask');

The issue seems to be with how cypress is communicating with the node js backend. Seemingly it uses network calls and messing with the network connection breaks it. That is what I think is happening, but could be other cause as well.

@jhnns
Copy link
Contributor

jhnns commented Jan 11, 2022

For documentation: The debugger protocol approach as described on the blog doesn't work since 7.3.0. This is probably the cause.

@myasul
Copy link

myasul commented Jan 31, 2022

I have implemented the following but nothing seems to work. Code can be seen below.

  • I used Cypress.automation('remote:debugger:protocol')
  • I also used cy.intercept with req.destroy
export class NetworkConnectivity {
    private static readonly affectedUrlsRegex = /(?<=localhost:\d+)\/(apollo|api)\/.+$/g

    public static goOffline () {
        const offlineDevtoolCommand = {
            command: 'Network.emulateNetworkConditions',
            params: {
                offline: true,
                latency: -1,
                downloadThroughput: -1,
                uploadThroughput: -1
            }
        }

        return cy
            .then(() => Cypress.automation('remote:debugger:protocol', { command: 'Network.enable' }))
            .then(() => cy.task('sendCommandsToServiceWorker', { command: 'Network.enable' }))
            .then(() => cy.task('sendCommandsToServiceWorker', offlineDevtoolCommand, { timeout: 120000 }))
            .then(() => Cypress.automation('remote:debugger:protocol', offlineDevtoolCommand))
            .then(() => {
                // Reference: https://docs.cypress.io/api/commands/intercept#Controlling-the-response

                // @ts-ignore
                Cypress.config('network', { isOnline: false })

                cy.intercept(this.affectedUrlsRegex, req => {
                    // @ts-ignore
                    const { isOnline } = Cypress.config('network')

                    if (!isOnline) {
                        req.destroy()
                    }
                })
            })
    }

Both of these would not entirely intercept the request and the request would still return a 200 response. This might be because we are using service workers. To try and fix this issue, I tried using puppeteer-core to access the active service workers and send out the commands. This worked when tested locally but fails in CI/CD with the error The Test Runner unexpectedly exited via a exit event with signal SIGSEGV but without further explanations about the failure.

const puppeteer = require('puppeteer-core')

const REMOTE_DEBUGGING_PORT = 9222

async function sendCommandsToServiceWorker ({ command, params }) {
    console.log(`Plugin started executing command ${command}, params: `, params)

    const browser = await puppeteer.connect({ browserURL: `http://localhost:${REMOTE_DEBUGGING_PORT}` })
    const targets = await browser.targets()
    const serviceWorkers = targets.filter((t) => t.type() === 'service_worker' || t.type() === 'other')

    const sendPromises = serviceWorkers.map(async (serviceWorker) => {
        const cdpSession = await serviceWorker.createCDPSession()
        await cdpSession.send(command, params)
    })

    await Promise.all(sendPromises)

    return null
}

module.exports = {
    sendCommandsToServiceWorker
}

Here is the screenshot of the CI/CD error.
image

I would really appreciate if anyone has solved this issue or have a workaround when using service workers.

@lolpez
Copy link

lolpez commented Apr 27, 2022

Guys it's 2022 👀

@cypress-bot cypress-bot bot added stage: backlog and removed stage: ready for work The issue is reproducible and in scope labels Apr 29, 2022
@aktibaba
Copy link

aktibaba commented May 9, 2022

This worked for me

cy.log('go offline');
return Cypress.automation('remote:debugger:protocol',
{
command: 'Network.enable',
}).then(() => {
Cypress.automation('remote:debugger:protocol',
{
command: 'Network.emulateNetworkConditions',
params: {
offline: true,
latency: -1,
downloadThroughput: -1,
uploadThroughput: -1,
},
})
return cy.wait(1000);
});

@laerteneto
Copy link

I can go offline, but I am not able to get back online again. So, following test cases fail.

However, it goes online/offline without any problems on Electron, but on Chrome and Edge it does not.

image

@aktibaba
Copy link

Copy exactly same from above and try again

@laerteneto
Copy link

I tried, but it still does not work on Chrome when I am trying to get back online, it only works on Electron.

I also tried the solution posted in here, but without success:
https://www.cypress.io/blog/2020/11/12/testing-application-in-offline-network-mode/

Copy exactly same from above and try again

@aktibaba
Copy link

aktibaba commented May 17, 2022

your code is missing return, premise returning nothing, try this

cy.log('go offline');
return Cypress.automation('remote:debugger:protocol',
{
command: 'Network.enable',
}).then(() => {
Cypress.automation('remote:debugger:protocol',
{
command: 'Network.emulateNetworkConditions',
params: {
offline: true,
latency: -1,
downloadThroughput: -1,
uploadThroughput: -1,
},
})
return cy.wait(1000);
});

@laerteneto
Copy link

your code is missing return, premise returning nothing, try this

cy.log('go offline'); return Cypress.automation('remote:debugger:protocol', { command: 'Network.enable', }).then(() => { Cypress.automation('remote:debugger:protocol', { command: 'Network.emulateNetworkConditions', params: { offline: true, latency: -1, downloadThroughput: -1, uploadThroughput: -1, }, }) return cy.wait(1000); });

I managed to get it offline, but the problem is to put it back online, in which it is working well only on Electron.

@ifn
Copy link

ifn commented May 23, 2022

Downgraded Cypress to 7.2.0. Seems to work.

UPD
Nope. Also rather torturous experience. I'll better try using forceNetworkError.

@AndreyGulevich
Copy link

Same problem: 7.9.0 version.

@DariusNorv
Copy link

I observe this problem in 10+ version in Electron runner as well.

@maximkoshelenko
Copy link

This one works for me

const goOffline = () => {
cy.log('go offline');
return Cypress.automation('remote:debugger:protocol',
{
command: 'Network.enable',
}).then(() => {
Cypress.automation('remote:debugger:protocol',
{
command: 'Network.emulateNetworkConditions',
params: {
offline: true,
latency: -1,
downloadThroughput: -1,
uploadThroughput: -1,
},
})
return true
});
};

@Shnrqpdr
Copy link

I'm facing this problem too. I'm using this code below:

network(params) {
    cy.log(`**Offline: ${params.offline}**`)
      .then(() => {
        Cypress.automation('remote:debugger:protocol', {
          command: 'Network.enable',
        });
      })
      .then(() => {
        Cypress.automation('remote:debugger:protocol', {
          command: 'Network.emulateNetworkConditions',
          params: {
            offline: params.offline,
            latency: 0,
            downloadThroughput: 0,
            uploadThroughput: 0,
            connectionType: 'none',
          },
        });
      });
  },

I've work with cypress 9.5.1. After going offline, can't get back online. Anyone has a solution for this?

@asmirnov-style
Copy link

Cypress v10.3.1 and still the same issue with going back Online.
Looks like it's turn Network for a second and then break connection.
Any ideas?

@Shnrqpdr
Copy link

Hello everyone

I have been stuck into this issue for at least 2 weeks ago and i got a "solution" to try simulate offline mode. The code is in the image below

image

Create this function on custom commands file of your project and call it using "cy.network({online: false})"
This work for me.
I hope that this workaround work for you too.

I'm using cypress 9.5.1

@lolpez
Copy link

lolpez commented Apr 25, 2023

Guys it's 2023 👀

@mostafijur022
Copy link

laerteneto
@laerteneto did you get any solution?

@buffcode
Copy link

buffcode commented May 24, 2023

Modifying the real onLine attribute, tested in Cypress 12.11.0:

// go offline
cy.window().then((win) => {
  cy.stub(win.navigator, 'onLine').value(false);
  cy.wrap(win).trigger('offline');
});

// go online
cy.window().then((win) => {
  cy.stub(win.navigator, 'onLine').value(true);
  cy.wrap(win).trigger('online');
});

@AntolinaGonzalez
Copy link

@laerteneto did you get any solution? Im stuck with go online too

@maxim-koshelenko
Copy link

maxim-koshelenko commented Sep 28, 2023

Modifying the real onLine attribute, tested in Cypress 12.11.0:

// go offline
cy.window().then((win) => {
  cy.stub(win.navigator, 'onLine').value(false);
  cy.wrap(win).trigger('offline');
});

// go online
cy.window().then((win) => {
  cy.stub(win.navigator, 'onLine').value(true);
  cy.wrap(win).trigger('online');
});

Confirming the workability of this solution.

Cypress v10, Chrome 117 (only in headless).

Don't forget to put go online into before, beforeEach and afterEach

@lolpez
Copy link

lolpez commented Apr 26, 2024

Guys it's 2024 👀

@snapris
Copy link

snapris commented Jul 24, 2024

I'm using this for Cypress 13.11.0 and Chrome v126: https://www.cypress.io/blog/2020/11/12/testing-application-in-offline-network-mode
Works for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
existing workaround type: feature New feature that does not currently exist
Projects
None yet
Development

Successfully merging a pull request may close this issue.