Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

can't use cypress to test app using using firestore local emulator

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.

like image 753
João Melo Avatar asked Dec 14 '19 15:12

João Melo


People also ask

Is it possible to run FireStore locally?

Until about less than a year ago running Firestore locally wasn't possible, but luckily things have changed with the release of Firebase Emulators. Although the main purpose of the emulators is to test Firebase's security rules, they can be adapted, with some tweaking, to test CRUD operations against a local database instance.

How do I connect to the Cloud Firestore emulator?

When connecting to the Cloud Firestore emulator from any other environment, you will need to specify a project ID. You can pass a project ID to initializeApp directly or set the GCLOUD_PROJECT environment variable.

How to use the firebase local emulator suite?

The Firebase Local Emulator Suite emulates products for a single Firebase project. To select the project to use, before you start the emulators, in the CLI run firebase use in your working directory. Or, you can pass the --project flag to each emulator command.

Does the Cloud Firestore emulator clear database content when shutting down?

Note: The Cloud Firestore emulator clears database contents when shut down. Since the offline cache of the Firestore SDK is not automatically cleared, you may want to disable local persistence in your emulator configuration to avoid discrepancies between the emulated database and local caches; in the Web SDK, persistence is disabled by default.


2 Answers

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.

like image 128
Gan Avatar answered Oct 19 '22 19:10

Gan


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;
    }),
like image 26
Andrew Allen Avatar answered Oct 19 '22 20:10

Andrew Allen