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
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
I tried to research answers but wasn't able to find one. Thanks in advance for any help.
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.
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.
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.
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.
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.
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;
}),
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With