Can we get some instructions for (if the ability exists), or a feature to enable the use of the firestore emulator from the browser?
Since the firestore emulator works well enough for testing nodeJs apps that use Google Cloud Firestore as a database, I tried to see if I could get it working within the browser as well. This would make Cypress testing of features on the front-end a lot better.
I copied the code from @firebase/testing/dist/index.cjs and removed the functions and imports related to the rules (so that the code could run in the browser). I imagined that, given that the testing app reaches the local firestore emulator, that a browser based firestore app would be able to reach it as well.
Instead of working, I'm this in the emulator console output.
firestore: Nov 06, 2018 4:01:58 PM io.gapi.emulators.netty.NotFoundHandler handleRequest
INFO: Unknown request URI: /google.firestore.v1beta1.Firestore/Write/channel?database=projects%2Fmytestapp%2Fdatabases%2F(default)&VER=8&RID=55074&CVER=22&X-HTTP-Session-Id=gsessionid&%24httpHeaders=X-Goog-Api-Client%3Agl-js%2F%20fire%2F5.5.0%0D%0A&zx=ux77gjnk9i4p&t=1
I'm assuming the firestore client handles node vs browser differently. It's hitting the emulator, but the emulator is 404ing on the request.
You're correct, the Firestore client has different behavior in Node and the browser. For now, the browser implementation of gRPC does not support bi-directional streams (which the Firestore API uses extensively).
The production Firestore service supports browsers using a different protocol. We are planning on adding support for that protocol to the emulator, but we don't have a specific roadmap for it yet.
Is there somewhere we can track browser support for Firestore emulator?
It doesn't seem like it has been released.
This is still in progress. I'm re-opening this issue to enable tracking.
@Johnhhorton I was looking into how to run the firebase infrastructure locally for testing with Cypress but looks like a lot of stuff is still in the pipeline.
Not sure if this would be practical for you but found this today: https://angularfirebase.com/lessons/testing-cloud-functions-in-firebase/ . You could create a test firebase project to run your Cypress tests, and switch over to self hosted emulator once it catches up.
Update: we're still working on this and it's one of our highest priority items. As Ryan said this comes down to adding support for another protocol, WebChannel, to our Firestore emulator.
If you're interested in trying out the Firestore emulator's experimental browser support, check out https://github.com/firebase/quickstart-nodejs/tree/master/firestore-emulator/browser-quickstart
Is it possible to use this experimental browser support with an authenticated user? For example, the firebase/testing module for node exposes an initializeTestApp function which accepts an auth argument for specifying a mocked user (see https://firebase.google.com/docs/firestore/security/test-rules-emulator#run_local_tests). It would be great if we could do something similar in the browser to test locally.
Apologies if this is not the best place to ask this question.
Yes, it should be possible.
a) You can use any normal authentication systems you'd like. I've successfully used anonymous auth and Facebook auth.
b) It's not particularly pleasant, but you can directly mock out the auth in the same way that we use in @firebase/testing? We will work on improving this. As a proof of concept, you can try
const accessToken = createUnsecuredJwt({
uid: "alice",
email: "[email protected]",
displayName: "Alice B. Connor"
});
const app = firebase.initializeApp({ projectId: "firestore-emulator-example" });
app.INTERNAL.getToken = () => Promise.resolve({ accessToken });
app.firestore().settings({ host: "localhost:50051", ssl: false });
where the helper method createUnsecuredJwt looks like
function createUnsecuredJwt(auth: object): string {
// Unsecured JWTs use "none" as the algorithm.
const header = {
alg: 'none',
kid: 'fakekid'
};
// Ensure that the auth payload has a value for 'iat'.
(auth as any).iat = (auth as any).iat || 0;
// Use `uid` field as a backup when `sub` is missing.
(auth as any).sub = (auth as any).sub || (auth as any).uid;
if (!(auth as any).sub) {
throw new Error("auth must be an object with a 'sub' or 'uid' field");
}
// Unsecured JWTs use the empty string as a signature.
const signature = '';
return [
base64.encodeString(JSON.stringify(header), /*webSafe=*/ false),
base64.encodeString(JSON.stringify(auth), /*webSafe=*/ false),
signature
].join('.');
}
And, of course, you can directly hardcode any Json Web Token you would like.
Since we do have browser support now I am going to close this general issue, but if anyone here runs into more specific things (like the interesting auth question by @jkeys089) please open a new issue! We are very happy to discuss.
Will this change be included in the gcloud beta gcloud beta emulators firestore? I have a docker image used for integration tests that extends gcloud-slim docker, but would love to be able to run the client against the dockerized emulator as well.
@bgarrett-rt I'm interested - you run your tests on a docker image with the emulator running? Was that easy?
@morgs32 My application code isn't in a docker, but I have the emulator running in a docker image. Then using the local emulator configuration bits, I can get my tests to talk to the emulator running in the docker. This part wasn't terrible to get working locally. That harder part was to figure out how to get this running in google cloud build as part of CI/CD. However, both locally and in cloud build, I have not figured out how to get the client to be able to run against the emulator. I need to write a blog post about it since it took me a loooooong time to figure out. However, you can get an idea for the set up here: https://gist.github.com/bgarrett-rt/628d63724cb8aaf852b391cd25058409
Note: There are probably more up to date emulator containers on docker hub. I did manage to find some after thee fact, but never got around to using them.
@bgarrett-rt yes we share the same emulator with them, but we have different release cycles so I am not sure if it's already been released there as well.
Also we would love to hear more about your journey to get Firebase testing in CI and on Cloud Build. If you're open to talking more email me at samstern [at] google [dot] com ... maybe we could even set up some time for you to video chat with the team and ~complain~ give us your feedback :-)
@ryanpbrewster Regarding using auth with the JS client SDK with the firebase:emulator. It looks like the app.INTERNAL.getToken does not exist in
import firebase from 'firebase/app'
import 'firebase/firestore'
import 'firebase/auth'
const app = firebase.initializeApp(firebaseConfig)
const db = app.firestore()
if (process.env.VUE_APP_FIREBASE_ENV === 'emulator' ) {
db.settings({
host: 'localhost:5002',
ssl: false,
experimentalForceLongPolling: true // to fix cypress
})
app.INTERNAL.getToken = () => Promise.resolve( ... ) // error
}
@glumb I believe that it's intentionally hidden, but you can bypass the type system here using
(app as any).INTERNAL.getToken = () => Promise.resolve(...)
As far as I know there is no well-supported way to hijack the Firebase SDK's auth system. @samtstern can you double-check that there's a feature request for a cleaner way to manually provide an auth token?
馃憢 @ryanpbrewster
@glumb the advice in this thread is from the very early days of browser support for the Firestore emulator. You should not be overriding app.INTERNAL.getToken() for any reason anymore, that was just a temporary hack.
Auth + Firestore should "just work" in the emulators now. If you're having issues with that on the latest version of firebase-tools please open a new issue.
INTERNAL doesn't exist on a recent version of firebase.
@samtstern I can't figure out how it is supposed to work now then. I just want to fake some user ID which can be used for firestore requests and used in firestore.rules as request.auth.uid in the emulator.
@firebase/testing is perfect for this, but doesn't work in the browser..
Edit: Got things working with auth().signInWithCustomToken. The token can be generated using firebase-admin:
import * as firebaseAdmin from 'firebase-admin';
firebaseAdmin.initializeApp({
projectId: 'projectId',
});
const uid = '12345';
firebaseAdmin.auth().createCustomToken(uid).then(console.log);
Most helpful comment
If you're interested in trying out the Firestore emulator's experimental browser support, check out https://github.com/firebase/quickstart-nodejs/tree/master/firestore-emulator/browser-quickstart