17

I have a app built with vue and firebase/firestore. I use the firebase emulator to local development and am trying to integrate my dev workflow with cypress. But i get a error in cypress that do not occur if i access the app from browser.

Firebase CLI version is 7.9.0 and Cypress version is "^3.8.0"

My npm scripts to load everything are below:

"start": "firebase emulators:exec --only firestore \"npm run dev:appandtest\"",
"dev:appandtest": "concurrently -n \"app,test\" -c \"bgYellow.black,bgWhite.black\" \"npm:dev:app\" \"npm:dev:test\"",
"dev:app": "webpack-dev-server --config build/webpack.dev.js",
"dev:test": "npx cypress open", 

The local server runs on port 9000 and the firebase emulator on port 8080.

After things are running, if i access the app from a normal browser everything is fine as this screen shows.

normal

enter image description here

Then i tried to run a basic cypress test with this code

    describe('The Home Page', function () {
      it('successfully loads', function () {
        cy.visit('/');
      });
    });

And i got the errors messages below:

    [2019-12-14T15:29:24.725Z]  @firebase/firestore: Firestore (6.6.2): Could not reach Cloud Firestore backend. Backend didn't respond within 10 seconds.
    This typically indicates that your device does not have a healthy Internet connection at the moment. The client will operate in offline mode until it is able to successfully connect to the backend.

    error.ts:166 Uncaught (in promise) FirebaseError: Failed to get document because the client is offline.
        at new FirestoreError (http://localhost:9000/bundle.js:11739:149)
        at Object.next (http://localhost:9000/bundle.js:16734:8)
        at next (http://localhost:9000/bundle.js:16725:4704)
        at http://localhost:9000/bundle.js:16430:411

I took a screenshot also: buggy

enter image description here

I tried to research answers but wasn't able to find one. Thanks in advance for any help.

João Melo
  • 784
  • 6
  • 16
  • Unfortunately I can't offer any help, but I am running into the same issue using latest version of `firebase-tools` (7.12.0) and cypress (3.8.1). I can connect to the emulator without issues from my browser, but within Cypress I can't connect to my emulator. – Alex Jan 13 '20 at 22:22
  • 3
    I am also experiencing this, so I created this issue in the Cypress repo with reproduction steps: https://github.com/cypress-io/cypress/issues/6350 – Scott Feb 06 '20 at 18:18
  • 1
    did you try with a different server port than 9000 - we had issue with cypress connecting to a localhost:9000 play framework app; worked fine when switched to another port. think it was an issue with the developers macbook running something else on the same port – James Render Feb 28 '20 at 16:19
  • Hi @AugustinGrigorov, I didn't figure it out. After that, I tried to use jest for testing but gave up because of excess in mock code needed for firebase. No blame to the framework, I think I am just noob at testing. I am overdue to give this use case another try. – João Melo Jun 24 '20 at 01:16

2 Answers2

18

The solution to this problem, at least for now, is to enable experimentalForceLongPolling, like this:

// NOTE: do NOT put this in production.
firebase.firestore().settings({ experimentalForceLongPolling: true })

Important: this is an experimental feature and you should put it in some conditional checks with environment variables. You should not use this in production environment.

The reason for this is best described here:

The default behavior of Firestore's web SDK is to make use of WebChannel's streaming mode. The client makes what looks like an XHR, but then the server will hold the response open for 60 seconds and send as many server-initiated responses as it can during that time window.

The experimentalForLongPolling option forces the server to send only a single response per request.

And here:

That is the same workaround we are using in cypress. I think the underlying problem is that Cypress is intercepting all network traffic so it can monitor and sometimes mock. However, the webchannel protocol used by firestore has multiple replies over the same http request. The Cypress code cannot handle this and will only forward the first reply and ignore the rest.

Gan
  • 4,827
  • 3
  • 34
  • 50
  • You may also try enabling `experimentalAutoDetectLongPolling`. Again, not for use in production (yet), but perhaps a little smarter than forcing long-polling always. Both of these keys worked for me, so I stayed with `experimentalAutoDetectLongPolling`. – AverageHelper Dec 30 '20 at 17:37
  • In my case, using `experimentalAutoDetectLongPolling` did solve the initial issue but cause some tests to fail with an uncaught exception `FirebaseError: Failed to get document because the client is offline.`. Switching to `experimentalForceLongPolling` did solve this problem. – sceee May 19 '21 at 16:37
  • 3
    web 9: `initializeFirestore(app, { experimentalForceLongPolling: true });`. This saved me after 4 days of trying to find the issue :( – Pablo Ezequiel Leone Feb 13 '22 at 19:17
  • To avoid it going into production, I went with `experimentalForceLongPolling: process.env.NODE_ENV === "development" ? true : false` – Steve Sep 10 '22 at 22:21
4

In the v9 new API you can't rely on
provideFirebaseApp(() => initializeApp(environment.firebase)),
where environment.firebase includes { experimentalAutoDetectLongPolling: true }.

Instead you have to explicitly do this in the provideFirestore method.

 provideFirestore(() => {
      let firestore;
      if (environment.useEmulators) {
        // bug: experimentalAutoDetectLongPolling not picked up via `getFirestore`
        const app = initializeApp(environment.firebase)
        firestore = initializeFirestore(app, {
          experimentalAutoDetectLongPolling: true
        })
        connectFirestoreEmulator(firestore, 'localhost', 8080)
      } else {
        firestore = getFirestore();
      }
      return firestore;
    }),
Andrew Allen
  • 6,512
  • 5
  • 30
  • 73